🌗 How to Set Up NativeWind with Expo React Native — Dark Mode + Themed Components + Custom Fonts

If you're building a React Native app with Expo and want to add Tailwind-like styling with theme support, NativeWind v4 is a perfect fit. It supports dark mode, custom fonts, and allows you to build design systems using Tailwind classes — right in your mobile app.

This guide walks you through how to:

💡 Tip: If you're using an older version of Expo (prior to SDK 53), consider using NativeWind v2 instead. NativeWind v4 requires Expo SDK 53 or later for full compatibility.


✅ Why Use NativeWind?


1. Install Dependencies

Install NativeWind and required libraries:

bash
1npm install nativewind react-native-reanimated@~3.17.4 react-native-safe-area-context@5.4.0
2npm install --save-dev tailwindcss@^3.4.17 prettier-plugin-tailwindcss@^0.5.11

📌 Note: You can use the latest versions of these packages, but the versions listed above are known to work perfectly with NativeWind v4 and Expo SDK 53, providing maximum compatibility and stability during setup.


2. Configure TailwindCSS

Create or edit tailwind.config.js:

js
1/** @type {import('tailwindcss').Config} */
2module.exports = {
3 content: [
4 "./App.tsx",
5 "./src/components/**/*.{js,jsx,ts,tsx}",
6 "./src/app/**/*.{js,jsx,ts,tsx}",
7 // Adjust these paths based on your project structure.
8 // This example assumes all source code is under the `src` directory.
9 ],
10 darkMode: "class",
11 presets: [require("nativewind/preset")],
12 theme: {
13 extend: {
14 // Setup font if you want to use custom font in React Native
15 fontFamily: {
16 sans: ["Montserrat-Regular"],
17 thin: ["Montserrat-Thin"],
18 extralight: ["Montserrat-ExtraLight"],
19 light: ["Montserrat-Light"],
20 medium: ["Montserrat-Medium"],
21 semibold: ["Montserrat-SemiBold"],
22 bold: ["Montserrat-Bold"],
23 extrabold: ["Montserrat-ExtraBold"],
24 black: ["Montserrat-Black"],
25 italic: ["Montserrat-Italic"],
26 },
27 colors: {
28 background: "hsl(var(--background) / <alpha-value>)",
29 foreground: "hsl(var(--foreground) / <alpha-value>)",
30 muted: "hsl(var(--muted) / <alpha-value>)",
31 "muted-foreground": "hsl(var(--muted-foreground) / <alpha-value>)",
32 popover: "hsl(var(--popover) / <alpha-value>)",
33 "popover-foreground": "hsl(var(--popover-foreground) / <alpha-value>)",
34 card: "hsl(var(--card) / <alpha-value>)",
35 "card-foreground": "hsl(var(--card-foreground) / <alpha-value>)",
36 border: "hsl(var(--border) / <alpha-value>)",
37 input: "hsl(var(--input) / <alpha-value>)",
38 primary: "hsl(var(--primary) / <alpha-value>)",
39 "primary-foreground": "hsl(var(--primary-foreground) / <alpha-value>)",
40 secondary: "hsl(var(--secondary) / <alpha-value>)",
41 "secondary-foreground":
42 "hsl(var(--secondary-foreground) / <alpha-value>)",
43 accent: "hsl(var(--accent) / <alpha-value>)",
44 "accent-foreground": "hsl(var(--accent-foreground) / <alpha-value>)",
45 destructive: "hsl(var(--destructive) / <alpha-value>)",
46 "destructive-foreground":
47 "hsl(var(--destructive-foreground) / <alpha-value>)",
48 ring: "hsl(var(--ring) / <alpha-value>)",
49 "chart-1": "hsl(var(--chart-1) / <alpha-value>)",
50 "chart-2": "hsl(var(--chart-2) / <alpha-value>)",
51 "chart-3": "hsl(var(--chart-3) / <alpha-value>)",
52 "chart-4": "hsl(var(--chart-4) / <alpha-value>)",
53 "chart-5": "hsl(var(--chart-5) / <alpha-value>)",
54 },
55 },
56 },
57};

3. Create a Global Stylesheet

Create a file styles/global.css and define your color variables:

css
1@tailwind base;
2@tailwind components;
3@tailwind utilities;
4
5@layer base {
6 :root {
7 --background: 0 0% 100%;
8 --foreground: 0 0% 13.33%;
9 --primary: 150 50% 45%;
10 --primary-foreground: 0 0% 100%;
11 /* ... other light theme colors */
12 }
13
14 // for dark theme
15 .dark:root {
16 --background: 0 0% 6.67%;
17 --foreground: 0 0% 93.33%;
18 --primary: 150.24 50% 40%;
19 --primary-foreground: 0 0% 93.33%;
20 /* ... other dark theme colors */
21 }
22}

Then import it in your root layout file:

tsx
1import "@/styles/global.css";

4. Configure Babel

Update your babel.config.js:

js
1module.exports = function (api) {
2 api.cache(true);
3 return {
4 presets: [
5 ["babel-preset-expo", { jsxImportSource: "nativewind" }],
6 "nativewind/babel",
7 ],
8 };
9};

5. Configure Metro

Create or update metro.config.js:

js
1const { getDefaultConfig } = require("expo/metro-config");
2const { withNativeWind } = require("nativewind/metro");
3
4const config = getDefaultConfig(__dirname);
5
6module.exports = withNativeWind(config, {
7 input: "./global.css",
8});

6. Add NativeWind Type Declarations

Create a file nativewind-env.d.ts in your root directory:

ts
1/// <reference types="nativewind/types" />

7. Load Custom Fonts

Download Montserrat fonts (on Google Fonts, etc) and place them in assets/fonts/, then load them in your layout:

ts
1const [fontsLoaded] = useFonts({
2 "Montserrat-Regular": require("@/assets/fonts/Montserrat-Regular.ttf"),
3 "Montserrat-Bold": require("@/assets/fonts/Montserrat-Bold.ttf"),
4 // ... other weights
5});

8. Create Theme Switcher Component

File: components/ThemeSwitcher.tsx

tsx
1import { useColorScheme } from "nativewind";
2import { Switch } from "react-native";
3import { ThemedText } from "./ThemedText";
4
5export default function ThemeSwitcher() {
6 const { colorScheme, toggleColorScheme } = useColorScheme();
7
8 return (
9 <>
10 <Switch
11 className="text-primary"
12 value={colorScheme === "dark"}
13 onValueChange={toggleColorScheme}
14 />
15 <ThemedText className="text-foreground">Dark mode</ThemedText>
16 </>
17 );
18}

9. Create or update ThemedText and ThemedView Components

components/ThemedText.tsx

tsx
1import React from "react";
2import { Text, TextProps } from "react-native";
3
4export type ThemedTextProps = TextProps & {
5 color?: string;
6 type?: "default" | "title" | "defaultSemiBold" | "subtitle" | "link";
7};
8
9export function ThemedText({
10 style,
11 color,
12 type = "default",
13 className,
14 ...rest
15}: ThemedTextProps) {
16 const typeClass = {
17 default: "text-base leading-6",
18 defaultSemiBold: "text-base leading-6 font-semibold",
19 title: "text-4xl font-bold leading-10",
20 subtitle: "text-xl font-bold",
21 link: "text-base leading-7 text-sky-600",
22 }[type];
23
24 const combinedClassName = [
25 typeClass,
26 color ? `text-${color}` : "text-foreground",
27 className,
28 ].join(" ");
29
30 return <Text className={combinedClassName} style={style} {...rest} />;
31}

components/ThemedView.tsx

tsx
1import { View, ViewProps } from "react-native";
2
3export type ThemedViewProps = ViewProps & {
4 color?: string;
5};
6
7export function ThemedView({
8 style,
9 color,
10 className,
11 ...otherProps
12}: ThemedViewProps) {
13 return (
14 <View
15 className={`${className} ${color ? `bg-${color}` : "bg-background"}`}
16 style={style}
17 {...otherProps}
18 />
19 );
20}

🔄 How to Use Themed Components

tsx
1<ThemedText color="muted-foreground" className="text-center">
2 Welcome to the app!
3</ThemedText>
4
5<ThemedView className="p-4 rounded-lg">
6 <ThemedText type="title">Hello World</ThemedText>
7</ThemedView>

🌙 Summary

You’ve now:

✅ Installed and configured NativeWind v4 in Expo ✅ Built a dark mode toggle switch ✅ Integrate custom fonts with TailwindCSS ✅ Created reusable components like ThemedText and ThemedView ✅ Added a global design system using Tailwind

This setup allows you to move fast while maintaining a consistent and theme-aware design across your app.