Table of Contents
- Overview
- Quickstart
- Prerequisites
- 1) Create or open an Expo app
- 2) Install dependencies
- 3) Initialize Tailwind config
- 4) Configure Tailwind
- 5) Add the NativeWind Babel plugin
- 6) Add TypeScript types (TS only)
- Minimal working example (App.tsx)
- Using with Expo Router (optional)
- Styling non-primitive components
- Common pitfalls
- Performance notes
- Debugging tips
- FAQ
- Next steps
Overview
NativeWind brings Tailwind-like utility classes to React Native. In Expo (managed workflow), setup is straightforward: install NativeWind and Tailwind CSS, add the NativeWind Babel plugin, configure Tailwind, and optionally add TypeScript types.
This guide targets Expo (managed) projects and NativeWind v2.x.
Quickstart
- Works with: Expo Go, development builds, Android, iOS, and web (via react-native-web).
- You do not run the Tailwind CLI in NativeWind. The Babel plugin compiles className utilities to React Native style objects.
Prerequisites
- Node 18+
- Expo SDK 49+ (recommended 50 or newer)
- TypeScript template (optional but used here)
1) Create or open an Expo app
# New app (TypeScript)
npx create-expo-app@latest my-app --template
cd my-app
If you already have an app, just continue in your project folder.
2) Install dependencies
# Using npm
npm install nativewind tailwindcss
# or with yarn
# yarn add nativewind tailwindcss
# or with pnpm
# pnpm add nativewind tailwindcss
3) Initialize Tailwind config
npx tailwindcss init
This creates tailwind.config.js.
4) Configure Tailwind
Edit tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./App.{js,jsx,ts,tsx}",
"./app/**/*.{js,jsx,ts,tsx}",
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {
colors: {
brand: {
500: "#5B8DEF",
600: "#3F6FD6",
},
},
},
},
plugins: [],
};
Notes:
- content globs help editors and tools; NativeWind’s Babel plugin does the actual class compilation.
- theme.extend lets you add custom tokens you can reference in classes (e.g., bg-brand-600).
5) Add the NativeWind Babel plugin
Edit babel.config.js:
module.exports = function (api) {
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: ["nativewind/babel"],
};
};
Restart the dev server after changing Babel config:
npx expo start -c
6) Add TypeScript types (TS only)
Edit tsconfig.json to include NativeWind’s types so className is recognized on React Native components:
{
"compilerOptions": {
"jsx": "react-jsx",
"types": ["nativewind/types"]
}
}
Minimal working example (App.tsx)
import { StatusBar } from "expo-status-bar";
import { useState } from "react";
import { Text, View, Pressable } from "react-native";
export default function App() {
const [on, setOn] = useState(false);
return (
<View className="flex-1 items-center justify-center bg-white dark:bg-black">
<Text className="text-2xl font-semibold text-gray-900 dark:text-gray-100">
NativeWind + Expo
</Text>
<Text className="mt-2 text-gray-600 dark:text-gray-300">
Toggle below to switch styles
</Text>
<Pressable
onPress={() => setOn((v) => !v)}
className={
on
? "mt-6 rounded-lg bg-brand-600 px-4 py-2"
: "mt-6 rounded-lg bg-gray-800 px-4 py-2"
}
>
<Text className="text-white font-medium">
{on ? "On" : "Off"}
</Text>
</Pressable>
<StatusBar style="auto" />
</View>
);
}
Run it:
npx expo start
Scan the QR with Expo Go or launch iOS/Android simulators.
Using with Expo Router (optional)
If you use expo-router, keep your content globs in tailwind.config.js pointing to the app directory (already included above). Example app/(tabs)/index.tsx:
import { View, Text } from "react-native";
export default function Home() {
return (
<View className="flex-1 items-center justify-center bg-white">
<Text className="text-xl font-bold">Home</Text>
</View>
);
}
Styling non-primitive components
Third-party components won’t automatically accept className. Wrap them with NativeWind’s styled helper:
import { styled } from "nativewind";
import { Pressable } from "react-native";
const SPressable = styled(Pressable);
export function Button(props: React.ComponentProps<typeof SPressable>) {
return (
<SPressable className="rounded-md bg-brand-600 px-3 py-2" {...props} />
);
}
Common pitfalls
- Missing Babel plugin: If className appears to do nothing, ensure babel.config.js includes "nativewind/babel" and restart with cache clear.
- TypeScript errors on className: Add "types": ["nativewind/types"] to tsconfig.json.
- Dynamic class generation: Avoid building class names with string concatenation like
"bg-" + color
. The compiler can’t see those. Prefer conditional selections of known classes. - Unsupported CSS utilities: React Native doesn’t support all CSS features. Utilities that map to unsupported properties (e.g., backdrop-blur, some filters) won’t work.
- Shadows on Android: Use elevation for Android shadows. Tailwind’s shadow utilities map to iOS shadows; Android requires
elevation
. - Dark mode: Use
dark:
variants with React Native Appearance or a theme provider. Ensure your app toggles color scheme or uses system preference. - Web-only assumptions: Some Tailwind plugins or pseudo-classes are web-only and won’t apply to native.
Performance notes
- Keep class lists static where possible: Static className strings are compiled at build time to optimized style arrays.
- Use simple conditionals: When variants are needed, select among a few static strings rather than concatenating fragments.
- Prefer fewer, meaningful utilities: Many overlapping utilities create larger style arrays. Consolidate where reasonable.
- Memoization: For frequently re-rendered lists, memoize rows and avoid recomputing variant choices.
- Hermes and production: Production builds with Hermes and the Babel transform yield the best runtime performance versus JS-based style assembly.
Debugging tips
- Force a clean restart after Babel or config changes:
npx expo start -c
. - Verify code reachability: The compiler only transforms classes present in your source files.
- Print styles: Temporarily pass both
className
and an equivalent inlinestyle
to compare visual output when diagnosing issues.
FAQ
- Do I need to run the Tailwind CLI or generate CSS?
- No. NativeWind’s Babel plugin handles class transformation for React Native.
- Does this work in Expo Go?
- Yes. As long as the Babel plugin is configured, Expo Go will render NativeWind styles.
- How do I add custom colors or fonts?
- Extend
theme
in tailwind.config.js, then reference utilities likebg-brand-600
orfont-[YourFontName]
if the font is loaded in Expo.
- Extend
- Can I use NativeWind on web via Expo?
- Yes. It works through react-native-web. Some utilities still reflect React Native’s style system rather than full CSS.
- What about class collisions or ordering?
- Later utilities in the class string win, similar to Tailwind on web. Order your utilities accordingly.
Next steps
- Add app-wide theming (light/dark) and extract reusable styled components.
- Define a small set of variant patterns for buttons, inputs, and headers to keep class names consistent across the app.