migrate-nativewind-to-uniwind
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMigrate NativeWind to Uniwind
将NativeWind迁移至Uniwind
Uniwind replaces NativeWind with better performance and stability. It requires Tailwind CSS 4 and uses CSS-based theming instead of JS config.
Uniwind以更优的性能和稳定性替代NativeWind。它要求使用Tailwind CSS 4,并采用基于CSS的主题方案而非JS配置。
Pre-Migration Checklist
迁移前检查清单
Before starting, read the project's existing config files to understand the current setup:
- (NativeWind version, dependencies)
package.json - /
tailwind.config.jstailwind.config.ts metro.config.jsbabel.config.js- or equivalent CSS entry file
global.css - or
nativewind-env.d.tsnativewind.d.ts - Any file using or
cssInteropfromremapPropsnativewind - Any file importing from
react-native-css-interop - Any ThemeProvider from NativeWind (usage)
vars()
开始迁移前,请阅读项目现有配置文件以了解当前设置:
- (NativeWind版本、依赖项)
package.json - /
tailwind.config.jstailwind.config.ts metro.config.jsbabel.config.js- 或等效的CSS入口文件
global.css - 或
nativewind-env.d.tsnativewind.d.ts - 任何使用中
nativewind或cssInterop的文件remapProps - 任何从导入的文件
react-native-css-interop - 任何来自NativeWind的ThemeProvider(使用的场景)
vars()
Step 1: Remove NativeWind and Related Packages
步骤1:移除NativeWind及相关包
Uninstall ALL of these packages (if present):
bash
npm uninstall nativewind react-native-css-interop卸载所有以下包(如果存在):
bash
npm uninstall nativewind react-native-css-interopor
或
yarn remove nativewind react-native-css-interop
yarn remove nativewind react-native-css-interop
or
或
bun remove nativewind react-native-css-interop
**CRITICAL**: `react-native-css-interop` is a NativeWind dependency that must be removed. It is commonly missed during migration. Search the entire codebase for any imports from it:
```bash
rg "react-native-css-interop" -g "*.{ts,tsx,js,jsx}"Remove every import and usage found.
bun remove nativewind react-native-css-interop
**关键注意事项**:`react-native-css-interop`是NativeWind的依赖项,必须移除。迁移过程中经常会遗漏这一步。搜索整个代码库查找任何来自它的导入:
```bash
rg "react-native-css-interop" -g "*.{ts,tsx,js,jsx}"删除找到的所有导入和用法。
Step 2: Install Uniwind and Tailwind 4
步骤2:安装Uniwind和Tailwind 4
bash
npm install uniwind tailwindcss@latestbash
npm install uniwind tailwindcss@latestor
或
yarn add uniwind tailwindcss@latest
yarn add uniwind tailwindcss@latest
or
或
bun add uniwind tailwindcss@latest
Ensure `tailwindcss` is version 4+.bun add uniwind tailwindcss@latest
确保`tailwindcss`版本为4+。Step 3: Update babel.config.js
步骤3:更新babel.config.js
Remove the NativeWind babel preset:
js
// REMOVE this line from presets array:
// 'nativewind/babel'No Uniwind babel preset is needed.
移除NativeWind的babel预设:
js
// 从presets数组中移除这一行:
// 'nativewind/babel'不需要Uniwind的babel预设。
Step 4: Update metro.config.js
步骤4:更新metro.config.js
Replace NativeWind's metro config with Uniwind's. must be the outermost wrapper.
withUniwindConfigBefore (NativeWind):
js
const { withNativeWind } = require('nativewind/metro');
module.exports = withNativeWind(config, { input: './global.css' });After (Uniwind):
js
const { getDefaultConfig } = require('expo/metro-config');
// For bare RN: const { getDefaultConfig } = require('@react-native/metro-config');
const { withUniwindConfig } = require('uniwind/metro');
const config = getDefaultConfig(__dirname);
module.exports = withUniwindConfig(config, {
cssEntryFile: './global.css',
polyfills: { rem: 14 },
});Always set to 14 to match NativeWind's default rem value and prevent spacing/sizing differences after migration.
polyfills.remIf the project uses custom themes beyond / (e.g. defined via NativeWind's or a custom ThemeProvider), register them with . Do NOT include or — they are added automatically:
lightdarkvars()extraThemeslightdarkjs
module.exports = withUniwindConfig(config, {
cssEntryFile: './global.css',
polyfills: { rem: 14 },
extraThemes: ['ocean', 'sunset', 'premium'],
});Options:
- (required): path to CSS entry file
cssEntryFile - (required for migration): set to
polyfills.remto match NativeWind's rem base14 - (required if project has custom themes): array of custom theme names — do NOT include
extraThemes/lightdark - (optional): path for generated TypeScript types, defaults to
dtsFile./uniwind-types.d.ts - (optional): log unsupported CSS properties during dev
debug
用Uniwind的配置替换NativeWind的metro配置。必须是最外层的包装器。
withUniwindConfig迁移前(NativeWind):
js
const { withNativeWind } = require('nativewind/metro');
module.exports = withNativeWind(config, { input: './global.css' });迁移后(Uniwind):
js
const { getDefaultConfig } = require('expo/metro-config');
// 纯RN项目:const { getDefaultConfig } = require('@react-native/metro-config');
const { withUniwindConfig } = require('uniwind/metro');
const config = getDefaultConfig(__dirname);
module.exports = withUniwindConfig(config, {
cssEntryFile: './global.css',
polyfills: { rem: 14 },
});务必将设置为14,以匹配NativeWind的默认rem值,避免迁移后出现间距/尺寸差异。
polyfills.rem如果项目使用/之外的自定义主题(例如通过NativeWind的或自定义ThemeProvider定义),请在中注册它们。不要包含或——它们会自动添加:
lightdarkvars()extraThemeslightdarkjs
module.exports = withUniwindConfig(config, {
cssEntryFile: './global.css',
polyfills: { rem: 14 },
extraThemes: ['ocean', 'sunset', 'premium'],
});选项说明:
- (必填):CSS入口文件路径
cssEntryFile - (迁移必填):设置为
polyfills.rem以匹配NativeWind的rem基准14 - (项目有自定义主题时必填):自定义主题名称数组——不要包含
extraThemes/lightdark - (可选):生成的TypeScript类型文件路径,默认为
dtsFile./uniwind-types.d.ts - (可选):开发期间记录不支持的CSS属性
debug
Step 5: Update global.css
步骤5:更新global.css
Replace NativeWind's Tailwind 3 directives with Tailwind 4 imports:
Before:
css
@tailwind base;
@tailwind components;
@tailwind utilities;After:
css
@import 'tailwindcss';
@import 'uniwind';用Tailwind 4的导入替换NativeWind的Tailwind 3指令:
迁移前:
css
@tailwind base;
@tailwind components;
@tailwind utilities;迁移后:
css
@import 'tailwindcss';
@import 'uniwind';Step 6: Update CSS Entry Import
步骤6:更新CSS入口导入
Ensure is imported in your main App component (e.g., ), NOT in the root / where you register the app — importing there breaks hot reload.
global.cssApp.tsxindex.tsindex.js确保在主App组件(如)中导入,不要在注册应用的根/中导入——在那里导入会破坏热重载。
global.cssApp.tsxindex.tsindex.jsStep 7: Delete NativeWind Type Definitions
步骤7:删除NativeWind类型定义
Delete or . Uniwind auto-generates its own types at the path specified by .
nativewind-env.d.tsnativewind.d.tsdtsFile删除或。Uniwind会自动在指定的路径生成自己的类型文件。
nativewind-env.d.tsnativewind.d.tsdtsFileStep 8: Delete tailwind.config.js
步骤8:删除tailwind.config.js
Remove / entirely. All theme config moves to CSS using Tailwind 4's directive.
tailwind.config.jstailwind.config.ts@themeMigrate custom theme values to :
global.cssBefore (tailwind.config.js):
js
module.exports = {
theme: {
extend: {
colors: {
primary: '#00a8ff',
secondary: '#273c75',
},
fontFamily: {
normal: ['Roboto-Regular'],
bold: ['Roboto-Bold'],
},
},
},
};After (global.css):
css
@import 'tailwindcss';
@import 'uniwind';
@theme {
--color-primary: #00a8ff;
--color-secondary: #273c75;
--font-normal: 'Roboto-Regular';
--font-bold: 'Roboto-Bold';
}Font families must specify a single font — React Native doesn't support font fallbacks.
完全移除 / 。所有主题配置将通过Tailwind 4的指令移至CSS中。
tailwind.config.jstailwind.config.ts@theme将自定义主题值迁移到:
global.css迁移前(tailwind.config.js):
js
module.exports = {
theme: {
extend: {
colors: {
primary: '#00a8ff',
secondary: '#273c75',
},
fontFamily: {
normal: ['Roboto-Regular'],
bold: ['Roboto-Bold'],
},
},
},
};迁移后(global.css):
css
@import 'tailwindcss';
@import 'uniwind';
@theme {
--color-primary: #00a8ff;
--color-secondary: #273c75;
--font-normal: 'Roboto-Regular';
--font-bold: 'Roboto-Bold';
}字体系列必须指定单个字体——React Native不支持字体回退。
Step 9: Remove ALL cssInterop and remapProps Usage
步骤9:移除所有cssInterop和remapProps用法
This is the most commonly missed step. Search the entire codebase:
bash
rg "cssInterop|remapProps" -g "*.{ts,tsx,js,jsx}"Replace every / call with Uniwind's :
cssInterop()remapProps()withUniwind()Before (NativeWind):
tsx
import { cssInterop } from 'react-native-css-interop';
import { Image } from 'expo-image';
cssInterop(Image, { className: 'style' });After (Uniwind):
tsx
import { withUniwind } from 'uniwind';
import { Image as ExpoImage } from 'expo-image';
export const Image = withUniwind(ExpoImage);withUniwindclassNamestyletsx
const StyledProgressBar = withUniwind(ProgressBar, {
width: {
fromClassName: 'widthClassName',
styleProperty: 'width',
},
});Define wrapped components at module level (not inside render functions). Each component should only be wrapped once:
-
Used in one file only — define the wrapped component in that same file:tsx
// screens/ProfileScreen.tsx import { withUniwind } from 'uniwind'; import { BlurView as RNBlurView } from '@react-native-community/blur'; const BlurView = withUniwind(RNBlurView); export function ProfileScreen() { return <BlurView className="flex-1" />; } -
Used across multiple files — wrap once in a shared module and re-export:tsx
// components/styled.ts import { withUniwind } from 'uniwind'; import { Image as ExpoImage } from 'expo-image'; import { LinearGradient as RNLinearGradient } from 'expo-linear-gradient'; export const Image = withUniwind(ExpoImage); export const LinearGradient = withUniwind(RNLinearGradient);Then import from the shared module everywhere:tsximport { Image, LinearGradient } from '@/components/styled';
Never call on the same component in multiple files — wrap once, import everywhere.
withUniwindIMPORTANT: Do NOT wrap components from or with — they already support out of the box. This includes , , , , , , , , etc. Only use for third-party components (e.g. , , ).
react-nativereact-native-reanimatedwithUniwindclassNameViewTextImageScrollViewFlatListPressableTextInputAnimated.ViewwithUniwindexpo-imageexpo-linear-gradient@react-native-community/blurIMPORTANT — accent- prefix for non-style color props: React Native components have props like , , that are NOT part of the object. To set these via Tailwind classes, use the prefix with the corresponding prop:
colortintColorbackgroundColorstyleaccent-*ClassNametsx
// color prop → colorClassName with accent- prefix
<ActivityIndicator
className="m-4"
size="large"
colorClassName="accent-blue-500 dark:accent-blue-400"
/>
// color prop on Button
<Button
colorClassName="accent-background"
title="Press me"
/>
// tintColor prop → tintColorClassName with accent- prefix
<Image
className="w-6 h-6"
tintColorClassName="accent-red-500"
source={icon}
/>Rule: accepts any Tailwind utility for style-based props. For non-style props (color, tintColor, etc.), use with the prefix. This applies to all built-in React Native components.
className{propName}ClassNameaccent-这是最常被遗漏的步骤。搜索整个代码库:
bash
rg "cssInterop|remapProps" -g "*.{ts,tsx,js,jsx}"将每个 / 调用替换为Uniwind的:
cssInterop()remapProps()withUniwind()迁移前(NativeWind):
tsx
import { cssInterop } from 'react-native-css-interop';
import { Image } from 'expo-image';
cssInterop(Image, { className: 'style' });迁移后(Uniwind):
tsx
import { withUniwind } from 'uniwind';
import { Image as ExpoImage } from 'expo-image';
export const Image = withUniwind(ExpoImage);withUniwindclassNamestyletsx
const StyledProgressBar = withUniwind(ProgressBar, {
width: {
fromClassName: 'widthClassName',
styleProperty: 'width',
},
});在模块级别定义包装后的组件(不要在渲染函数内部)。每个组件只应被包装一次:
-
仅在一个文件中使用——在同一文件中定义包装后的组件:tsx
// screens/ProfileScreen.tsx import { withUniwind } from 'uniwind'; import { BlurView as RNBlurView } from '@react-native-community/blur'; const BlurView = withUniwind(RNBlurView); export function ProfileScreen() { return <BlurView className="flex-1" />; } -
在多个文件中使用——在共享模块中包装一次并重新导出:tsx
// components/styled.ts import { withUniwind } from 'uniwind'; import { Image as ExpoImage } from 'expo-image'; import { LinearGradient as RNLinearGradient } from 'expo-linear-gradient'; export const Image = withUniwind(ExpoImage); export const LinearGradient = withUniwind(RNLinearGradient);然后在所有需要的地方从共享模块导入:tsximport { Image, LinearGradient } from '@/components/styled';
不要在多个文件中对同一组件调用——只包装一次,在各处导入使用。
withUniwind重要提示:非样式颜色属性的accent-前缀:React Native组件有一些不属于对象的属性,如、、。要通过Tailwind类设置这些属性,请使用前缀和对应的属性:
stylecolortintColorbackgroundColoraccent-*ClassNametsx
// color属性 → 使用colorClassName和accent-前缀
<ActivityIndicator
className="m-4"
size="large"
colorClassName="accent-blue-500 dark:accent-blue-400"
/>
// Button组件的color属性
<Button
colorClassName="accent-background"
title="Press me"
/>
// tintColor属性 → 使用tintColorClassName和accent-前缀
<Image
className="w-6 h-6"
tintColorClassName="accent-red-500"
source={icon}
/>规则:接受任何用于样式类属性的Tailwind工具类。对于非样式属性(color、tintColor等),使用并加上前缀。这适用于所有内置React Native组件。
className{propName}ClassNameaccent-Step 10: Migrate NativeWind Theme Variables
步骤10:迁移NativeWind主题变量
Before (NativeWind JS themes with ):
vars()tsx
import { vars } from 'nativewind';
export const themes = {
light: vars({
'--color-primary': '#00a8ff',
'--color-typography': '#000',
}),
dark: vars({
'--color-primary': '#273c75',
'--color-typography': '#fff',
}),
};
// In JSX:
<View style={themes[colorScheme]}>After (Uniwind CSS themes):
css
@layer theme {
:root {
@variant light {
--color-primary: #00a8ff;
--color-typography: #000;
}
@variant dark {
--color-primary: #273c75;
--color-typography: #fff;
}
}
}IMPORTANT: All theme variants must define the exact same set of CSS variables. If defines and , then (and any custom theme) must also define both. Mismatched variables will cause a Uniwind runtime error.
light--color-primary--color-typographydarkNo ThemeProvider wrapper needed. Remove the NativeWind or wrapper from JSX. Keep React Navigation's if used.
<ThemeProvider>vars()<ThemeProvider>If the project has custom themes beyond light/dark (e.g. , ), you must:
oceanpremium- Define them in CSS using :
@variant
css
@layer theme {
:root {
@variant ocean {
--color-primary: #0ea5e9;
--color-background: #0c4a6e;
}
}
}- Register them in metro.config.js via (skip
extraThemes/light— they are auto-added):dark
js
module.exports = withUniwindConfig(config, {
cssEntryFile: './global.css',
polyfills: { rem: 14 },
extraThemes: ['ocean', 'premium'],
});迁移前(使用的NativeWind JS主题):
vars()tsx
import { vars } from 'nativewind';
export const themes = {
light: vars({
'--color-primary': '#00a8ff',
'--color-typography': '#000',
}),
dark: vars({
'--color-primary': '#273c75',
'--color-typography': '#fff',
}),
};
// 在JSX中:
<View style={themes[colorScheme]}>迁移后(Uniwind CSS主题):
css
@layer theme {
:root {
@variant light {
--color-primary: #00a8ff;
--color-typography: #000;
}
@variant dark {
--color-primary: #273c75;
--color-typography: #fff;
}
}
}重要提示:所有主题变体必须定义完全相同的CSS变量集。如果定义了和,那么(以及任何自定义主题)也必须定义这两个变量。变量不匹配会导致Uniwind运行时错误。
light--color-primary--color-typographydark不需要ThemeProvider包装器。从JSX中移除NativeWind的或包装器。如果使用了React Navigation的则保留。
<ThemeProvider>vars()<ThemeProvider>如果项目有light/dark之外的自定义主题(如、),必须:
oceanpremium- 在CSS中使用定义它们:
@variant
css
@layer theme {
:root {
@variant ocean {
--color-primary: #0ea5e9;
--color-background: #0c4a6e;
}
}
}- 在metro.config.js中通过注册它们(跳过
extraThemes/light——它们会自动添加):dark
js
module.exports = withUniwindConfig(config, {
cssEntryFile: './global.css',
polyfills: { rem: 14 },
extraThemes: ['ocean', 'premium'],
});Step 11: Migrate Safe Area Utilities
步骤11:迁移安全区域工具类
NativeWind's safe area classes need explicit setup in Uniwind:
tsx
import { SafeAreaProvider, SafeAreaListener } from 'react-native-safe-area-context';
import { Uniwind } from 'uniwind';
export default function App() {
return (
<SafeAreaProvider>
<SafeAreaListener
onChange={({ insets }) => {
Uniwind.updateInsets(insets);
}}
>
<View className="pt-safe px-safe">
{/* content */}
</View>
</SafeAreaListener>
</SafeAreaProvider>
);
}NativeWind的安全区域类在Uniwind中需要显式设置:
tsx
import { SafeAreaProvider, SafeAreaListener } from 'react-native-safe-area-context';
import { Uniwind } from 'uniwind';
export default function App() {
return (
<SafeAreaProvider>
<SafeAreaListener
onChange={({ insets }) => {
Uniwind.updateInsets(insets);
}}
>
<View className="pt-safe px-safe">
{/* 内容 */}
</View>
</SafeAreaListener>
</SafeAreaProvider>
);
}Step 12: Verify rem Value
步骤12:验证rem值
NativeWind uses 14px as the base rem, Uniwind defaults to 16px. Step 4 already sets in metro config to preserve NativeWind's spacing. If the user explicitly wants Uniwind's default (16px), they can remove the polyfill — but warn them that all spacing/sizing will shift.
polyfills: { rem: 14 }NativeWind使用14px作为基准rem,Uniwind默认是16px。步骤4已在metro配置中设置以保留NativeWind的间距。如果用户明确希望使用Uniwind的默认值(16px),可以移除该polyfill,但需提醒他们所有间距/尺寸都会发生变化。
polyfills: { rem: 14 }Step 13: Handle className Deduplication
步骤13:处理className重复
Uniwind does NOT auto-deduplicate conflicting classNames (NativeWind did). If your codebase relies on override patterns like , set up a utility.
className={`p-4 ${overrideClass}`}cnFirst, check if the project already has a helper (common in shadcn/ui projects):
cnbash
rg "export function cn|export const cn" -g "*.{ts,tsx,js}"If it exists, keep it as-is. If not, install dependencies and create it:
bash
npm install tailwind-merge clsxCreate (or wherever utils live in the project):
lib/cn.tsts
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}Usage:
tsx
import { cn } from '@/lib/cn';
<View className={cn('p-4 bg-white', props.className)} />
<Text className={cn('text-base', isActive && 'text-blue-500', disabled && 'opacity-50')} />Use instead of raw — it handles conditional classes, arrays, and falsy values via before deduplicating with .
cntwMergeclsxtailwind-mergeUniwind不会自动去重冲突的className(NativeWind会)。如果代码库依赖类似的覆盖模式,请设置工具。
className={`p-4 ${overrideClass}`}cn首先检查项目是否已有助手(在shadcn/ui项目中很常见):
cnbash
rg "export function cn|export const cn" -g "*.{ts,tsx,js}"如果已存在,保持原样。如果不存在,安装依赖并创建:
bash
npm install tailwind-merge clsx创建(或项目中工具类所在的位置):
lib/cn.tsts
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}用法:
tsx
import { cn } from '@/lib/cn';
<View className={cn('p-4 bg-white', props.className)} />
<Text className={cn('text-base', isActive && 'text-blue-500', disabled && 'opacity-50')} />使用而非原生——它会通过处理条件类、数组和假值,然后用去重。
cntwMergeclsxtailwind-mergeStep 14: Update Animated Class Names
步骤14:迁移动画类名
If the project used NativeWind / transition class patterns, migrate those to explicit usage. Uniwind OSS does not provide NativeWind-style animated class behavior.
animated-*react-native-reanimatedUse this migration guide section as the source of truth:
如果项目使用了NativeWind的 / 过渡类模式,请将它们迁移为直接使用。Uniwind开源版本不提供NativeWind风格的动画类行为。
animated-*react-native-reanimated请参考此迁移指南部分作为权威来源:
Step 15: Clean Up Remaining NativeWind References
步骤15:清理剩余的NativeWind引用
Final sweep — search for and remove any remaining references:
bash
rg "nativewind|NativeWind|native-wind" -g "*.{ts,tsx,js,jsx,json,css}"Check for:
- NativeWind imports in any file
- in
nativewind(devDependencies too)package.json - in
react-native-css-interoppackage.json - NativeWind babel preset in
babel.config.js - NativeWind metro wrapper in
metro.config.js - or
nativewind-env.d.tsfilesnativewind.d.ts - Any or
cssInterop()callsremapProps() - Any imports from
vars()nativewind
最终检查——搜索并移除所有剩余引用:
bash
rg "nativewind|NativeWind|native-wind" -g "*.{ts,tsx,js,jsx,json,css}"检查以下内容:
- 任何文件中的NativeWind导入
- 中的
package.json(包括devDependencies)nativewind - 中的
package.jsonreact-native-css-interop - 中的NativeWind babel预设
babel.config.js - 中的NativeWind metro包装器
metro.config.js - 或
nativewind-env.d.ts文件nativewind.d.ts - 任何或
cssInterop()调用remapProps() - 任何来自的
nativewind导入vars()
Uniwind APIs & Patterns
Uniwind API与模式
useUniwind — Theme Access (re-renders on change)
useUniwind — 主题访问(主题变化时重新渲染)
tsx
import { useUniwind } from 'uniwind';
const { theme, hasAdaptiveThemes } = useUniwind();
// theme: current theme name — "light", "dark", "system", or custom
// hasAdaptiveThemes: true if app follows system color schemeUse for: displaying theme name in UI, conditional rendering by theme, side effects on theme change.
tsx
import { useUniwind } from 'uniwind';
const { theme, hasAdaptiveThemes } = useUniwind();
// theme: 当前主题名称 — "light", "dark", "system"或自定义主题
// hasAdaptiveThemes: 如果应用跟随系统配色方案则为true适用场景:在UI中显示主题名称、按主题条件渲染、主题变化时的副作用处理。
Uniwind Static API — Theme Access (no re-render)
Uniwind静态API — 主题访问(无重新渲染)
Access theme info without causing re-renders:
tsx
import { Uniwind } from 'uniwind';
Uniwind.currentTheme // "light", "dark", "system", or custom
Uniwind.hasAdaptiveThemes // true if following system color schemeUse for: logging, analytics, imperative logic outside render.
无需导致重新渲染即可访问主题信息:
tsx
import { Uniwind } from 'uniwind';
Uniwind.currentTheme // "light", "dark", "system"或自定义主题
Uniwind.hasAdaptiveThemes // 如果跟随系统配色方案则为true适用场景:日志记录、分析、渲染外的命令式逻辑。
useResolveClassNames — Convert classNames to Style Objects
useResolveClassNames — 将className转换为样式对象
Converts Tailwind classes into React Native style objects. Use when working with components that don't support and can't be wrapped with (e.g. react-navigation theme config):
classNamewithUniwindtsx
import { useResolveClassNames } from 'uniwind';
const headerStyle = useResolveClassNames('bg-blue-500');
const cardStyle = useResolveClassNames('bg-white dark:bg-gray-900');
<Stack.Navigator
screenOptions={{
headerStyle: headerStyle,
cardStyle: cardStyle,
}}
/>将Tailwind类转换为React Native样式对象。适用于不支持且无法用包装的组件(如react-navigation主题配置):
classNamewithUniwindtsx
import { useResolveClassNames } from 'uniwind';
const headerStyle = useResolveClassNames('bg-blue-500');
const cardStyle = useResolveClassNames('bg-white dark:bg-gray-900');
<Stack.Navigator
screenOptions={{
headerStyle: headerStyle,
cardStyle: cardStyle,
}}
/>useCSSVariable — Access CSS Variables in JS
useCSSVariable — 在JS中访问CSS变量
Retrieve CSS variable values programmatically. Variable must be prefixed with and match a variable defined in :
--global.csstsx
import { useCSSVariable } from 'uniwind';
const primaryColor = useCSSVariable('--color-primary');
const spacing = useCSSVariable('--spacing-4');Use for: animations, third-party library configs, calculations with design tokens.
以编程方式获取CSS变量值。变量必须以为前缀,且与中定义的变量匹配:
--global.csstsx
import { useCSSVariable } from 'uniwind';
const primaryColor = useCSSVariable('--color-primary');
const spacing = useCSSVariable('--spacing-4');适用场景:动画、第三方库配置、使用设计令牌的计算。
CSS Functions — Custom Utilities
CSS函数 — 自定义工具类
Define custom utilities using device-aware CSS functions like , , :
hairlineWidth()fontScale()pixelRatio()css
@theme {
--width-hairline: hairlineWidth();
}Then use as:
<View className="w-hairline" />使用感知设备的CSS函数(如、、)定义自定义工具类:
hairlineWidth()fontScale()pixelRatio()css
@theme {
--width-hairline: hairlineWidth();
}然后这样使用:
<View className="w-hairline" />Platform Selectors
平台选择器
Apply styles conditionally per platform using , , , prefixes:
ios:android:web:native:tsx
<View className="ios:bg-red-500 android:bg-blue-500 web:bg-green-500">
<Text className="ios:text-white android:text-white web:text-black">
Platform-specific styles
</Text>
</View>使用、、、前缀按平台条件应用样式:
ios:android:web:native:tsx
<View className="ios:bg-red-500 android:bg-blue-500 web:bg-green-500">
<Text className="ios:text-white android:text-white web:text-black">
平台专属样式
</Text>
</View>Theme Switching
主题切换
By default Uniwind follows the system color scheme (adaptive themes). To switch themes programmatically:
tsx
import { Uniwind } from 'uniwind';
Uniwind.setTheme('dark'); // force dark
Uniwind.setTheme('light'); // force light
Uniwind.setTheme('system'); // follow system (default)
Uniwind.setTheme('ocean'); // custom theme (must be in extraThemes)默认情况下,Uniwind跟随系统配色方案(自适应主题)。要以编程方式切换主题:
tsx
import { Uniwind } from 'uniwind';
Uniwind.setTheme('dark'); // 强制深色模式
Uniwind.setTheme('light'); // 强制浅色模式
Uniwind.setTheme('system'); // 跟随系统(默认)
Uniwind.setTheme('ocean'); // 自定义主题(必须在extraThemes中注册)Style Based on Themes — Prefer CSS Variables
基于主题的样式 — 优先使用CSS变量
Prefer using CSS variable-based classes over explicit dark:/light: variants. Instead of:
tsx
// Avoid this pattern
<View className="light:bg-white dark:bg-black" />Define a CSS variable and use it directly:
css
@layer theme {
:root {
@variant light { --color-background: #ffffff; }
@variant dark { --color-background: #000000; }
}
}tsx
// Preferred — automatically adapts to theme
<View className="bg-background" />This is cleaner, easier to maintain, and works automatically with custom themes too.
优先使用基于CSS变量的类而非显式的dark:/light:变体。不要这样写:
tsx
// 避免这种模式
<View className="light:bg-white dark:bg-black" />定义一个CSS变量并直接使用:
css
@layer theme {
:root {
@variant light { --color-background: #ffffff; }
@variant dark { --color-background: #000000; }
}
}tsx
// 推荐方式——自动适配主题
<View className="bg-background" />这种方式更简洁、更易于维护,并且也能自动适配自定义主题。
Runtime CSS Variable Updates
运行时CSS变量更新
Update theme variables at runtime, e.g. based on user preferences or API responses:
tsx
import { Uniwind } from 'uniwind';
// Preconfigure theme based on user input or API response
Uniwind.updateCSSVariables('light', {
'--color-primary': '#ff6600',
'--color-background': '#1a1a2e',
});This pattern should be used only when the app has real runtime theming needs (for example, user-selected brand colors or API-driven themes).
在运行时更新主题变量,例如基于用户偏好或API响应:
tsx
import { Uniwind } from 'uniwind';
// 根据用户输入或API响应预配置主题
Uniwind.updateCSSVariables('light', {
'--color-primary': '#ff6600',
'--color-background': '#1a1a2e',
});仅当应用有实际的运行时主题需求时才使用此模式(例如用户选择的品牌颜色或API驱动的主题)。
Variants with tailwind-variants
使用tailwind-variants实现变体
For component variants and compound variants, use the library:
tailwind-variantstsx
import { tv } from 'tailwind-variants';
const button = tv({
base: 'px-4 py-2 rounded-lg',
variants: {
color: {
primary: 'bg-primary text-white',
secondary: 'bg-secondary text-white',
},
size: {
sm: 'text-sm',
lg: 'text-lg px-6 py-3',
},
},
});
<Pressable className={button({ color: 'primary', size: 'lg' })} />对于组件变体和复合变体,使用库:
tailwind-variantstsx
import { tv } from 'tailwind-variants';
const button = tv({
base: 'px-4 py-2 rounded-lg',
variants: {
color: {
primary: 'bg-primary text-white',
secondary: 'bg-secondary text-white',
},
size: {
sm: 'text-sm',
lg: 'text-lg px-6 py-3',
},
},
});
<Pressable className={button({ color: 'primary', size: 'lg' })} />Monorepo Support
monorepo支持
If the project is a monorepo, add directives in so Tailwind scans packages outside the CSS entry file's directory (only if that directory has components with Tailwind classes):
@sourceglobal.csscss
@import 'tailwindcss';
@import 'uniwind';
@source "../../packages/ui/src";
@source "../../packages/shared/src";如果项目是monorepo,在中添加指令,以便Tailwind扫描CSS入口文件目录之外的包(仅当该目录包含带有Tailwind类的组件时):
global.css@sourcecss
@import 'tailwindcss';
@import 'uniwind';
@source "../../packages/ui/src";
@source "../../packages/shared/src";FAQ
常见问题
Custom Fonts: Uniwind maps className to font-family only — font files must be loaded separately (expo-font plugin in or for bare RN). Font family names in must exactly match filenames (without extension). Use platform media queries for per-platform fonts:
app.jsonreact-native-asset@themecss
@media ios { --font-sans: 'SF Pro Text'; }
@media android { --font-sans: 'Roboto-Regular'; }Data Selectors: Use for prop-based styling. Only equality checks supported:
data-[prop=value]:utilitytsx
<View data-state={isOpen ? 'open' : 'closed'} className="data-[state=open]:bg-muted/50" />global.css Location in Expo Router: Place at project root and import in root layout (). If placed in , components outside need directives. Tailwind scans from location.
app/_layout.tsxapp/@sourceglobal.cssFull App Reloads on CSS Changes: Metro can't hot-reload files with many providers. Move import deeper in the component tree (e.g. navigation root or home screen) to fix.
global.cssGradients: Built-in support, no extra deps needed. Use . For , use to get colors — won't work since gradient props are arrays.
bg-gradient-to-r from-red-500 via-yellow-500 to-green-500expo-linear-gradientuseCSSVariablewithUniwindStyle Specificity: Inline always overrides . Use for static styles, inline only for truly dynamic values. Avoid mixing both for the same property.
styleclassNameclassNameSerialization Errors (): Clear caches: . Common causes: complex configs, circular CSS variable references.
Failed to serialize javascript objectwatchman watch-del-all 2>/dev/null; rm -rf node_modules/.cache && npx expo start --clear@themeMetro unstable_enablePackageExports Conflicts: Some apps (crypto etc.) disable this, breaking Uniwind. Use selective resolver:
js
config.resolver.unstable_enablePackageExports = false;
config.resolver.resolveRequest = (context, moduleName, platform) => {
if (['uniwind', 'culori'].some((prefix) => moduleName.startsWith(prefix))) {
return context.resolveRequest({ ...context, unstable_enablePackageExports: true }, moduleName, platform);
}
return context.resolveRequest(context, moduleName, platform);
};Safe Area Classes: , , , , , , , etc. Also supports (min spacing) and (extra spacing) variants.
p-safept-safepb-safepx-safepy-safem-safemt-safe-or-{value}-offset-{value}Next.js: Not officially supported. Uniwind is for Metro and Vite. Community plugin: . For Next.js, use standard Tailwind CSS and share design tokens.
uniwind-plugin-nextVite: Supported since v1.2.0. Use plugin alongside .
uniwind/vite@tailwindcss/viteUI Kits: HeroUI Native, react-native-reusables and Gluestack 4.1+ works great with Uniwind
自定义字体:Uniwind仅将className映射到font-family——字体文件必须单独加载(在中使用expo-font插件,或在纯RN项目中使用)。中的字体系列名称必须与文件名完全匹配(不含扩展名)。使用平台媒体查询实现跨平台字体:
app.jsonreact-native-asset@themecss
@media ios { --font-sans: 'SF Pro Text'; }
@media android { --font-sans: 'Roboto-Regular'; }数据选择器:使用实现基于属性的样式。仅支持相等性检查:
data-[prop=value]:utilitytsx
<View data-state={isOpen ? 'open' : 'closed'} className="data-[state=open]:bg-muted/50" />Expo Router中的global.css位置:放在项目根目录并在根布局()中导入。如果放在中,外部组件需要指令。Tailwind从所在位置开始扫描。
app/_layout.tsxapp/@sourceglobal.cssCSS变更导致的全应用重载:Metro无法热重载包含多个提供者的文件。将的导入移至组件树的更深处(如导航根或首页)以解决此问题。
global.css渐变:内置支持,无需额外依赖。使用。对于,使用获取颜色——无法工作,因为渐变属性是数组。
bg-gradient-to-r from-red-500 via-yellow-500 to-green-500expo-linear-gradientuseCSSVariablewithUniwind样式优先级:内联始终覆盖。用于静态样式,内联样式仅用于真正动态的值。避免为同一属性同时使用两者。
styleclassNameclassName序列化错误():清除缓存:。常见原因:复杂的配置、CSS变量循环引用。
Failed to serialize javascript objectwatchman watch-del-all 2>/dev/null; rm -rf node_modules/.cache && npx expo start --clear@themeMetro unstable_enablePackageExports冲突:某些应用(如加密类)禁用此功能,导致Uniwind无法工作。使用选择性解析器:
js
config.resolver.unstable_enablePackageExports = false;
config.resolver.resolveRequest = (context, moduleName, platform) => {
if (['uniwind', 'culori'].some((prefix) => moduleName.startsWith(prefix))) {
return context.resolveRequest({ ...context, unstable_enablePackageExports: true }, moduleName, platform);
}
return context.resolveRequest(context, moduleName, platform);
};安全区域类:、、、、、、等。还支持(最小间距)和(额外间距)变体。
p-safept-safepb-safepx-safepy-safem-safemt-safe-or-{value}-offset-{value}Next.js:官方不支持。Uniwind适用于Metro和Vite。社区插件:。对于Next.js,使用标准Tailwind CSS并共享设计令牌。
uniwind-plugin-nextVite:从v1.2.0开始支持。将插件与一起使用。
uniwind/vite@tailwindcss/viteUI套件:HeroUI Native、react-native-reusables和Gluestack 4.1+与Uniwind配合良好
Known Issues & Gotchas
已知问题与注意事项
- data- attributes*: Uniwind supports syntax for conditional styling, similar to NativeWind.
data-[prop=value]:utility - Animated styles: Migrate NativeWind animated classes to directly. Uniwind Pro has built-in Reanimated support.
react-native-reanimated
- *data-属性:Uniwind支持语法实现条件样式,与NativeWind类似。
data-[prop=value]:utility - 动画样式:将NativeWind动画类迁移为直接使用。Uniwind Pro内置Reanimated支持。
react-native-reanimated
Verification
验证
After migration, verify:
- (clear Metro cache) or with expo
npx react-native start --reset-cachenpx expo start -c - All screens render correctly on iOS and Android
- Theme switching works (light/dark)
- Custom fonts load correctly
- Safe area insets apply properly
- No console warnings about missing styles
- No remaining imports from or
nativewindreact-native-css-interop
IMPORTANT: Do NOT guess Uniwind APIs. If you are unsure about any Uniwind API, hook, component, or configuration option, fetch and verify against the official docs: https://docs.uniwind.dev/llms-full.txt
迁移完成后,验证以下内容:
- (清除Metro缓存),或使用Expo的
npx react-native start --reset-cachenpx expo start -c - 所有屏幕在iOS和Android上都能正确渲染
- 主题切换正常工作(浅色/深色)
- 自定义字体加载正常
- 安全区域内边距正确应用
- 没有关于缺失样式的控制台警告
- 没有来自或
nativewind的剩余导入react-native-css-interop
重要提示:不要猜测Uniwind API。如果对任何Uniwind API、钩子、组件或配置选项有疑问,请查阅并验证官方文档:https://docs.uniwind.dev/llms-full.txt