بنقرة واحدة
expo-tailwind-setup
Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.
Set up high-performance development servers with Hot Module Replacement. Use when creating dev servers for web apps, setting up React Fast Refresh, or configuring API servers with live reload.
Modern JavaScript/TypeScript development with Bun runtime. Covers package management, bundling, testing, and migration from Node.js. Use when working with Bun, optimizing JS/TS development speed, o...
Initialize a new Bun project with TypeScript and optimal configuration. Use when starting a new Bun project or converting a directory to a Bun project.
Configure Bun's built-in test runner with Jest-compatible APIs. Use when setting up testing infrastructure, writing unit/integration/snapshot tests, migrating from Jest, or configuring test coverage. 3-10x faster than Jest.
Guides the agent through upgrading a Capacitor app project to a newer major version. Supports upgrades from Capacitor 4 through 8, including multi-version jumps. Covers automated upgrade via the Capacitor CLI and manual step-by-step fallback for each version. Do not use for plugin library upgrade or non-Capacitor mobile frameworks.
| name | expo-tailwind-setup |
| description | Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling |
| version | 1.0.0 |
| license | MIT |
This guide covers setting up Tailwind CSS v4 in Expo using react-native-css and NativeWind v5 for universal styling across iOS, Android, and Web.
This setup uses:
# Install dependencies
npx expo install tailwindcss@^4 nativewind@5.0.0-preview.2 react-native-css@0.0.0-nightly.5ce6396 @tailwindcss/postcss tailwind-merge clsx
Add resolutions for lightningcss compatibility:
// package.json
{
"resolutions": {
"lightningcss": "1.30.1"
}
}
Create or update metro.config.js:
// metro.config.js
const {getDefaultConfig} = require('expo/metro-config')
const {withNativewind} = require('nativewind/metro')
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname)
module.exports = withNativewind(config, {
// inline variables break PlatformColor in CSS variables
inlineVariables: false,
// We add className support manually
globalClassNamePolyfill: false,
})
Create postcss.config.mjs:
// postcss.config.mjs
export default {
plugins: {
'@tailwindcss/postcss': {},
},
}
Create src/global.css:
@import 'tailwindcss/theme.css' layer(theme);
@import 'tailwindcss/preflight.css' layer(base);
@import 'tailwindcss/utilities.css';
/* Platform-specific font families */
@media android {
:root {
--font-mono: monospace;
--font-rounded: normal;
--font-serif: serif;
--font-sans: normal;
}
}
@media ios {
:root {
--font-mono: ui-monospace;
--font-serif: ui-serif;
--font-sans: system-ui;
--font-rounded: ui-rounded;
}
}
With Tailwind v4 and NativeWind v5, you do NOT need a babel.config.js for Tailwind. Remove any NativeWind babel presets if present:
// DELETE babel.config.js if it only contains NativeWind config
// The following is NO LONGER needed:
// module.exports = function (api) {
// api.cache(true);
// return {
// presets: [
// ["babel-preset-expo", { jsxImportSource: "nativewind" }],
// "nativewind/babel",
// ],
// };
// };
Since react-native-css requires explicit CSS element wrapping, create reusable components:
src/tw/index.tsx)import {
useCssElement,
useNativeVariable as useFunctionalVariable,
} from 'react-native-css'
import {
View as RNView,
Text as RNText,
Pressable as RNPressable,
ScrollView as RNScrollView,
TouchableHighlight as RNTouchableHighlight,
TextInput as RNTextInput,
StyleSheet,
} from 'react-native'
import Animated from 'react-native-reanimated'
import {Link as RouterLink} from 'expo-router'
import React from 'react'
// CSS-enabled Link
export const Link = (
props: React.ComponentProps<typeof RouterLink> & {className?: string},
) => {
return useCssElement(RouterLink, props, {className: 'style'})
}
Link.Trigger = RouterLink.Trigger
Link.Menu = RouterLink.Menu
Link.MenuAction = RouterLink.MenuAction
Link.Preview = RouterLink.Preview
// CSS Variable hook
export const useCSSVariable =
process.env.EXPO_OS !== 'web'
? useFunctionalVariable
: (variable: string) => `var(${variable})`
// View
export type ViewProps = React.ComponentProps<typeof RNView> & {
className?: string
}
export const View = (props: ViewProps) => {
return useCssElement(RNView, props, {className: 'style'})
}
View.displayName = 'CSS(View)'
// Text
export const Text = (
props: React.ComponentProps<typeof RNText> & {className?: string},
) => {
return useCssElement(RNText, props, {className: 'style'})
}
Text.displayName = 'CSS(Text)'
// ScrollView
export const ScrollView = (
props: React.ComponentProps<typeof RNScrollView> & {
className?: string
contentContainerClassName?: string
},
) => {
return useCssElement(RNScrollView, props, {
className: 'style',
contentContainerClassName: 'contentContainerStyle',
})
}
ScrollView.displayName = 'CSS(ScrollView)'
// Pressable
export const Pressable = (
props: React.ComponentProps<typeof RNPressable> & {className?: string},
) => {
return useCssElement(RNPressable, props, {className: 'style'})
}
Pressable.displayName = 'CSS(Pressable)'
// TextInput
export const TextInput = (
props: React.ComponentProps<typeof RNTextInput> & {className?: string},
) => {
return useCssElement(RNTextInput, props, {className: 'style'})
}
TextInput.displayName = 'CSS(TextInput)'
// AnimatedScrollView
export const AnimatedScrollView = (
props: React.ComponentProps<typeof Animated.ScrollView> & {
className?: string
contentClassName?: string
contentContainerClassName?: string
},
) => {
return useCssElement(Animated.ScrollView, props, {
className: 'style',
contentClassName: 'contentContainerStyle',
contentContainerClassName: 'contentContainerStyle',
})
}
// TouchableHighlight with underlayColor extraction
function XXTouchableHighlight(
props: React.ComponentProps<typeof RNTouchableHighlight>,
) {
const {underlayColor, ...style} = StyleSheet.flatten(props.style) || {}
return (
<RNTouchableHighlight
underlayColor={underlayColor}
{...props}
style={style}
/>
)
}
export const TouchableHighlight = (
props: React.ComponentProps<typeof RNTouchableHighlight>,
) => {
return useCssElement(XXTouchableHighlight, props, {className: 'style'})
}
TouchableHighlight.displayName = 'CSS(TouchableHighlight)'
src/tw/image.tsx)import Animated from 'react-native-reanimated'
import {useCssElement} from 'react-native-css'
import {Image as RNImage} from 'expo-image'
import {StyleSheet} from 'react-native'
import React from 'react'
const AnimatedExpoImage = Animated.createAnimatedComponent(RNImage)
export type ImageProps = React.ComponentProps<typeof Image>
function CSSImage(props: React.ComponentProps<typeof AnimatedExpoImage>) {
// @ts-expect-error: Remap objectFit style to contentFit property
const {objectFit, objectPosition, ...style} =
StyleSheet.flatten(props.style) || {}
return (
<AnimatedExpoImage
contentFit={objectFit}
contentPosition={objectPosition}
{...props}
source={
typeof props.source === 'string' ? {uri: props.source} : props.source
}
// @ts-expect-error: Style is remapped above
style={style}
/>
)
}
export const Image = (
props: React.ComponentProps<typeof CSSImage> & {className?: string},
) => {
return useCssElement(CSSImage, props, {className: 'style'})
}
Image.displayName = 'CSS(Image)'
src/tw/animated.tsx)import RNAnimated from 'react-native-reanimated'
import * as TW from './index'
export const Animated = {
...RNAnimated,
View: RNAnimated.createAnimatedComponent(TW.View),
}
Import CSS-wrapped components from your tw directory:
import {View, Text, ScrollView, Image} from '@/tw'
export default function MyScreen() {
return (
<ScrollView className="flex-1 bg-white">
<View className="p-4 gap-4">
<Text className="text-xl font-bold text-gray-900">Hello Tailwind!</Text>
<Image
className="w-full h-48 rounded-lg object-cover"
source={{uri: 'https://example.com/image.jpg'}}
/>
</View>
</ScrollView>
)
}
Add custom theme variables in your global.css using @theme:
@layer theme {
@theme {
/* Custom fonts */
--font-rounded: 'SF Pro Rounded', sans-serif;
/* Custom line heights */
--text-xs--line-height: calc(1em / 0.75);
--text-sm--line-height: calc(1.25em / 0.875);
--text-base--line-height: calc(1.5em / 1);
/* Custom leading scales */
--leading-tight: 1.25em;
--leading-snug: 1.375em;
--leading-normal: 1.5em;
}
}
Use platform media queries for platform-specific styling:
@media ios {
:root {
--font-sans: system-ui;
--font-rounded: ui-rounded;
}
}
@media android {
:root {
--font-sans: normal;
--font-rounded: normal;
}
}
Create a CSS file for Apple semantic colors:
/* src/css/sf.css */
@layer base {
html {
color-scheme: light;
}
}
:root {
/* Accent colors with light/dark mode */
--sf-blue: light-dark(rgb(0 122 255), rgb(10 132 255));
--sf-green: light-dark(rgb(52 199 89), rgb(48 209 89));
--sf-red: light-dark(rgb(255 59 48), rgb(255 69 58));
/* Gray scales */
--sf-gray: light-dark(rgb(142 142 147), rgb(142 142 147));
--sf-gray-2: light-dark(rgb(174 174 178), rgb(99 99 102));
/* Text colors */
--sf-text: light-dark(rgb(0 0 0), rgb(255 255 255));
--sf-text-2: light-dark(rgb(60 60 67 / 0.6), rgb(235 235 245 / 0.6));
/* Background colors */
--sf-bg: light-dark(rgb(255 255 255), rgb(0 0 0));
--sf-bg-2: light-dark(rgb(242 242 247), rgb(28 28 30));
}
/* iOS native colors via platformColor */
@media ios {
:root {
--sf-blue: platformColor(systemBlue);
--sf-green: platformColor(systemGreen);
--sf-red: platformColor(systemRed);
--sf-gray: platformColor(systemGray);
--sf-text: platformColor(label);
--sf-text-2: platformColor(secondaryLabel);
--sf-bg: platformColor(systemBackground);
--sf-bg-2: platformColor(secondarySystemBackground);
}
}
/* Register as Tailwind theme colors */
@layer theme {
@theme {
--color-sf-blue: var(--sf-blue);
--color-sf-green: var(--sf-green);
--color-sf-red: var(--sf-red);
--color-sf-gray: var(--sf-gray);
--color-sf-text: var(--sf-text);
--color-sf-text-2: var(--sf-text-2);
--color-sf-bg: var(--sf-bg);
--color-sf-bg-2: var(--sf-bg-2);
}
}
Then use in components:
<Text className="text-sf-text">Primary text</Text>
<Text className="text-sf-text-2">Secondary text</Text>
<View className="bg-sf-bg">...</View>
Use the useCSSVariable hook:
import {useCSSVariable} from '@/tw'
function MyComponent() {
const blue = useCSSVariable('--sf-blue')
return <View style={{borderColor: blue}} />
}
@tailwindcss/postcss instead of tailwindcss@import "tailwindcss/..." instead of @tailwind directives@theme in CSS instead of tailwind.config.jsuseCssElement for className supportwithNativewind with different options (inlineVariables: false)useCssElementwithNativewind appliedplatformColor() in @media ios blockslight-dark() for web/AndroidAdd className to component props:
type Props = React.ComponentProps<typeof RNView> & {className?: string}