Set Up Tailwind CSS for Expo with NativeWind
Sets up Tailwind CSS v4 in Expo via react-native-css and NativeWind v5 for universal iOS/Android/web styling, no Babel required.
Why it matters
Integrate Tailwind CSS v4 into your Expo project using react-native-css and NativeWind v5 for a unified styling solution across iOS, Android, and web platforms.
Outcomes
What it gets done
Configure Metro and PostCSS for Tailwind CSS v4.
Set up global CSS with preflight and utilities.
Implement CSS-wrapped components for React Native elements.
Ensure compatibility with Expo and NativeWind v5.
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/ag-expo-tailwind-setup | bash Overview
Tailwind CSS Setup for Expo with react-native-css
Configures Tailwind CSS v4 in Expo via react-native-css and NativeWind v5 - Metro/PostCSS config, global CSS theme variables, and CSS-enabled component wrappers - for universal iOS/Android/web styling without Babel. Use when setting up or migrating to Tailwind CSS v4 styling in an Expo project across iOS, Android, and web.
What it does
Tailwind CSS Setup for Expo with react-native-css covers configuring Tailwind CSS v4 in an Expo app using react-native-css and NativeWind v5 for one styling setup that works across iOS, Android, and web. The stack is Tailwind CSS v4 (CSS-first configuration), react-native-css (the CSS runtime for React Native), NativeWind v5 (the Metro transformer), and @tailwindcss/postcss (the PostCSS plugin for Tailwind v4).
Installation is npx expo install tailwindcss@^4 nativewind@5.0.0-preview.2 react-native-css@0.0.0-nightly.5ce6396 @tailwindcss/postcss tailwind-merge clsx, plus a lightningcss version pin in package.json resolutions (autoprefixer isn't needed since Expo uses lightningcss, and PostCSS ships with Expo by default). Configuration spans three files: metro.config.js wraps the default Expo Metro config with withNativewind, setting inlineVariables: false (inline variables break PlatformColor in CSS variables) and globalClassNamePolyfill: false (className support is added manually); postcss.config.mjs registers the @tailwindcss/postcss plugin; and src/global.css imports Tailwind's theme/preflight/utilities layers plus platform-specific font-family variables for Android and iOS via @media android/@media ios blocks. Critically, Tailwind v4 + NativeWind v5 needs no babel.config.js for styling - any NativeWind Babel preset should be removed.
Because react-native-css requires explicit CSS element wrapping, the setup defines reusable wrapped components in a src/tw/ directory: View, Text, ScrollView, Pressable, TextInput, Link (wrapping expo-router's Link), TouchableHighlight (with underlay-color extraction), and AnimatedScrollView, each built via useCssElement mapping className to style (and contentContainerClassName to contentContainerStyle where relevant); a separate Image wrapper remaps CSS objectFit/objectPosition to Expo Image's contentFit/contentPosition; and an Animated.View wrapper composes react-native-reanimated with the CSS-enabled View. These are imported and used like <View className="p-4 gap-4">...</View> from @/tw.
Custom theme variables (fonts, line heights, leading scales) are declared in global.css under @layer theme { @theme { ... } }. Platform-specific styling uses @media ios/@media android blocks to swap CSS variable values. Apple system colors are set up as a dedicated src/css/sf.css defining light/dark-aware CSS variables via light-dark() with iOS-only overrides using platformColor(systemBlue)-style native color references, registered as Tailwind theme colors (--color-sf-blue: var(--sf-blue)) so they're usable as className="text-sf-text" or className="bg-sf-bg". CSS variables can also be read in JavaScript via the useCSSVariable hook (useFunctionalVariable natively, or var(--x) on web).
Key differences from NativeWind v4 / Tailwind v3: no babel.config.js; @tailwindcss/postcss instead of the tailwindcss PostCSS plugin; @import "tailwindcss/..." instead of @tailwind directives; @theme in CSS instead of tailwind.config.js; components must be wrapped with useCssElement for className support; and Metro config uses withNativewind with different options. Troubleshooting covers styles not applying (check the CSS import in the app entry, component wrapping, and Metro config), platform colors not working (use platformColor() inside @media ios, fall back to light-dark() elsewhere), and TypeScript errors (add className?: string to component prop types).
When to use - and when NOT to
Use this skill when setting up Tailwind CSS v4 styling in an Expo app using react-native-css and NativeWind v5, when configuring Metro, PostCSS, global CSS, or package versions for Expo + Tailwind, or when a single styling setup is needed across iOS, Android, and web in an Expo project. Do not treat the output as a substitute for environment-specific validation, testing, or expert review, and stop to ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
Who it's for
Expo/React Native developers adopting Tailwind CSS v4 who want universal iOS/Android/web styling without a Babel-based NativeWind v4 setup, and who need working component wrappers and platform-aware theme variables (including Apple system colors) from the start.
FAQ
Common questions
Discussion
Questions & comments ยท 0
Sign In Sign in to leave a comment.