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,722
フォーク
56

インストール方法

デフォルトでは、最初にソースを確認する 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で見る