Skip to main content

migrate-nativewind-to-uniwind

Migrate a React Native project from NativeWind to Uniwind. Use when the user wants to replace NativeWind with Uniwind, upgrade from NativeWind, switch to Uniwind, or mentions NativeWind-to-Uniwind migration. Handles package removal, config migration, Tailwind 4 upgrade, cssInterop removal, theme conversion, and all breaking changes.

소스 정보

저장소
uni-stack/uniwind
최근 소스 활동
2026년 5월 14일 11:27
감지된 SKILL.md 언어
영어
스타
1,733
포크
60

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
migrate-nativewind-to-uniwind
description
Migrate a React Native project from NativeWind to Uniwind. Use when the user wants to replace NativeWind with Uniwind, upgrade from NativeWind, switch to Uniwind, or mentions NativeWind-to-Uniwind migration. Handles package removal, config migration, Tailwind 4 upgrade, cssInterop removal, theme conversion, and all breaking changes.
# Migrate NativeWind to Uniwind Uniwind replaces NativeWind with better performance and stability. It requires **Tailwind CSS 4** and uses CSS-based theming instead of JS config. ## Pre-Migration Checklist Before starting, read the project's existing config files to understand the current setup: - `package.json` (NativeWind version, dependencies) - `tailwind.config.js` / `tailwind.config.ts` - `metro.config.js` - `babel.config.js` - `global.css` or equivalent CSS entry file - `nativewind-env.d.ts` or `nativewind.d.ts` - Any file using `cssInterop` or `remapProps` from `nativewind` - Any file importing from `react-native-css-interop` - Any ThemeProvider from NativeWind (`vars()` usage) ## Step 1: Remove NativeWind and Related Packages Uninstall ALL of these packages (if present): ```bash npm uninstall nativewind react-native-css-interop # or 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. ## Step 2: Install Uniwind and Tailwind 4 ```bash npm install uniwind tailwindcss@latest # or yarn add uniwind tailwindcss@latest # or bun add uniwind tailwindcss@latest ``` Ensure `tailwindcss` is version 4+. ## Step 3: Update babel.config.js Remove the NativeWind babel preset: ```js // REMOVE this line from presets array: // 'nativewind/babel' ``` No Uniwind babel preset is needed. ## Step 4: Update metro.config.js Replace NativeWind's metro config with Uniwind's. `withUniwindConfig` must be the **outermost wrapper**. **Before (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 }, }); ``` `cssEntryFile` must be a **relative path string** from project root (e.g. `./global.css` or `./app/global.css`). Do **not** use absolute paths or `path.resolve(...)` / `path.join(...)` for this option. ```js // ❌ Broken cssEntryFile: path.resolve(__dirname, 'app', 'global.css') // ✅ Correct cssEntryFile: './app/global.css' ``` **Always set `polyfills.rem` to 14** to match NativeWind's default rem value and prevent spacing/sizing differences after migration. If the project uses custom themes beyond `light`/`dark` (e.g. defined via NativeWind's `vars()` or a custom ThemeProvider), register them with `extraThemes`. Do NOT include `light` or `dark` — they are added automatically: ```js module.exports = withUniwindConfig(config, { cssEntryFile: './global.css', polyfills: { rem: 14 }, extraThemes: ['ocean', 'sunset', 'premium'], }); ``` Options: - `cssEntryFile` (required): relative path string to CSS entry file (from project root) - `polyfills.rem` (required for migration): set to `14` to match NativeWind's rem base - `extraThemes` (required if project has custom themes): array of custom theme names — do NOT include `light`/`dark` - `dtsFile` (optional): path for generated TypeScript types, defaults to `./uniwind-types.d.ts` - `debug` (optional): log unsupported CSS properties during dev ## Step 5: Update 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'; ``` ## Step 6: Update CSS Entry Import Ensure `global.css` is imported in your main App component (e.g., `App.tsx`), NOT in the root `index.ts`/`index.js` where you register the app — importing there breaks hot reload. ## Step 7: Delete NativeWind Type Definitions Delete `nativewind-env.d.ts` or `nativewind.d.ts`. Uniwind auto-generates its own types at the path specified by `dtsFile`. ## Step 8: Delete tailwind.config.js Remove `tailwind.config.js` / `tailwind.config.ts` entirely. All theme config moves to CSS using Tailwind 4's `@theme` directive. Migrate custom theme values to `global.css`: **Before (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. ## Step 9: Remove ALL cssInterop and remapProps Usage **This is the most commonly missed step.** Search the entire codebase: ```bash rg "cssInterop|remapProps" -g "*.{ts,tsx,js,jsx}" ``` Replace every `cssInterop()` / `remapProps()` call with Uniwind's `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); ``` `withUniwind` automatically maps `className` → `style` and other common props. For custom prop mappings: ```tsx 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: ```tsx import { Image, LinearGradient } from '@/components/styled'; ``` Never call `withUniwind` on the same component in multiple files — wrap once, import everywhere. **IMPORTANT**: Do NOT wrap components from `react-native` or `react-native-reanimated` with `withUniwind` — they already support `className` out of the box. This includes `View`, `Text`, `Image`, `ScrollView`, `FlatList`, `Pressable`, `TextInput`, `Animated.View`, etc. Only use `withUniwind` for **third-party** components (e.g. `expo-image`, `expo-linear-gradient`, `@react-native-community/blur`). **IMPORTANT — accent- prefix for non-style color props**: React Native components have props like `color`, `tintColor`, `backgroundColor` that are NOT part of the `style` object. To set these via Tailwind classes, use the `accent-` prefix with the corresponding `*ClassName` prop: ```tsx // 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: `className` accepts any Tailwind utility for style-based props. For non-style props (color, tintColor, etc.), use `{propName}ClassName` with the `accent-` prefix. This applies to all built-in React Native components. ## Step 10: Migrate NativeWind Theme Variables **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 `light` defines `--color-primary` and `--color-typography`, then `dark` (and any custom theme) must also define both. Mismatched variables will cause a Uniwind runtime error. No ThemeProvider wrapper needed. Remove the NativeWind `<ThemeProvider>` or `vars()` wrapper from JSX. Keep React Navigation's `<ThemeProvider>` if used. If the project used nested theme wrappers to preview or force a theme for a specific subtree (for example a demo card, settings preview, or side-by-side theme comparison), use Uniwind Pro's `ScopedTheme` instead of changing the global theme: ```tsx import { ScopedTheme } from 'uniwind'; <ScopedTheme theme="dark"> <PreviewCard /> </ScopedTheme> ``` If the project has **custom themes beyond light/dark** (e.g. `ocean`, `premium`), you must: 1. Define them in CSS using `@variant`: ```css @layer theme { :root { @variant ocean { --color-primary: #0ea5e9; --color-background: #0c4a6e; } } } ``` 2. Register them in metro.config.js via `extraThemes` (skip `light`/`dark` — they are auto-added): ```js module.exports = withUniwindConfig(config, { cssEntryFile: './global.css', polyfills: { rem: 14 }, extraThemes: ['ocean', 'premium'], }); ``` ## Step 11: Migrate Safe Area Utilities 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> ); } ``` ## Step 12: Verify rem Value NativeWind uses 14px as the base rem, Uniwind defaults to 16px. Step 4 already sets `polyfills: { rem: 14 }` 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. ## Step 13: Handle className Deduplication Uniwind does NOT auto-deduplicate conflicting classNames (NativeWind did). If your codebase relies on override patterns like ``className={`p-4 ${overrideClass}`}``, set up a `cn` utility. First, check if the project already has a `cn` helper (common in shadcn/ui projects): ```bash 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 clsx ```
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기