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 con
By uni-stack · 1,895 installs
npx skills add uni-stack/uniwind --skill migrate-nativewind-to-uniwind
Source repository · Upstream listing
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):
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:
Remove every import and usage found.
Step 2: Install Uniwind and Tailwind 4
Ensure tailwindcss is version 4+.
Step 3: Update babel.config.js
Remove the NativeWind babel preset:
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):
After (Uniwind):
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.
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:
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:
After:
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):
After (global.css):
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:
Replace every cssInterop() / remapProps() call with Uniwind's withUniwind() :
Before (NativeWind):
After (Uniwind):
withUniwind automatically maps className → style and other common props. For custom prop mappings:
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:
Used across multiple files — wrap once in a shared module and re export:
Then import from the shared module everywhere:
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:
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() ):
After (Uniwind CSS themes):
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:
If the project has custom themes beyond light/dark (e.g. ocean , premium ), you must:
1. Define them in CSS using @variant :
2. Register them in metro.config.js via extraThemes (skip light / dark — they are auto added):
Step 11: Migrate Safe Area Utilities
NativeWind's safe area classes need explicit setup in Uniwind:
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):
If it exists, keep it as is. If not, install dependencies and create it:
Create lib/cn.ts (or wherever utils live in the project):
Usage:
Use cn instead of raw twMerge — it handles conditional classes, arrays, and falsy values via clsx before deduplicating with tailwind merge .
Important utilities are also supported in Uniwind. If migrated NativeWind code intentionally forces an override with Tailwind's important modifier, keep it:
Important utilities override non important utilities for the same style property. Inline style still has the highest priority, even over important className utilities.
Step 14: Update Animated Class Names
If the project used NativeWind animated / transition class patterns, migrate those to explicit react native reanimated usage. Uniwind OSS does not provide NativeWind style animated class behavior.
Use this migration guide section as the source of truth:
https://docs.uniwind.dev/migration from nativewind
Step 15: Clean Up Remaining NativeWind References
Final sweep — search for and remove any remaining references:
Check for:
NativeWind imports in any file
nativewind in package.json (devDependencies too)
react native css interop in package.json
NativeWind babel preset in babel.config.js
NativeWind metro wrapper in metro.config.js
nativewind env.d.ts or nativewind.d.ts files
Any cssInterop() or remapProps() calls
Any vars() imports from nativewind
Uniwind APIs & Patterns
useUniwind — Theme Access (re renders on change)
Docs: https://docs.uniwind.dev/api/use uniwind
Use for: displaying theme name in UI, conditional rendering by theme, side effects on theme change.
Uniwind Static API — Theme Access (no re render)
Access theme info without causing re renders:
Use for: logging, analytics, imperative logic outside render.
useResolveClassNames — Convert classNames to Style Objects
Docs: https://docs.uniwind.dev/api/use resolve class names
Converts Tailwind classes into React Native style objects. Use when working with components that don't support className and can't be wrapped with withUniwind (e.g. react navigation theme config):
useCSSVariable — Access CSS Variables in JS
Docs: https://docs.uniwind.dev/api/use css variable
Retrieve CSS variable values programmatically. Variable must be prefixed with and match a variable defined in global.css :
Use for: animations, third party library configs, calculations with design tokens.
CSS Functions — Custom Utilities
Docs: https://docs.uniwind.dev/api/css functions
Define custom utilities using device aware CSS functions like hairlineWidth() , fontScale() , pixelRatio() . These can be used everywhere (custom CSS classes, @utility , etc.) — but NOT inside @theme {} (which only accepts static values). Use @utility to create reusable Tailwind style classes:
Then use as: <View className="w hairline h hairline" /
Platform Selectors
Docs: https://docs.uniwind.dev/api/platform select
Apply styles conditionally per platform using ios: , android: , web: , native: prefixes:
Theme Switching
Docs: https://docs.uniwind.dev/theming/basics
By default Uniwind follows the system color scheme (adaptive themes). To switch themes programmatically:
ScopedTheme — Theme a Subtree Only
Docs: https://docs.uniwind.dev/api/scoped themes
Use ScopedTheme when the project needs a different theme for only part of the UI (component previews, themed sections, nested demos) without changing the app wide theme:
Important behavior:
Nearest ScopedTheme wins (nested scopes are supported)
Hooks like useUniwind , useResolveClassNames , and useCSSVariable resolve against the nearest scoped theme
withUniwind wrapped third party components inside the scope also resolve themed values from that scope
Custom theme names can be used in ScopedTheme (must be defined in extraThemes )
Style Based on Themes — Prefer CSS Variables
Docs: https://docs.uniwind.dev/theming/style based on themes
Prefer using CSS variable based classes over explicit dark:/light: variants. Instead of:
Define a CSS variable and use it directly:
This is cleaner, easier to maintain, and works automatically with custom themes too.
Runtime CSS Variable Updates
Docs: https://docs.uniwind.dev/theming/update css variables
Update theme variables at runtime, e.g. based on user preferences or API responses:
This pattern should be used only when the app has real runtime theming needs (for example, user selected brand colors or API driven themes).
Variants with tailwind variants
Docs: https://docs.uniwind.dev/tailwind basics advanced pattern variants and compound variants
For component variants and compound variants, use the tailwind variants library:
Monorepo Support
Docs: https://docs.uniwind.dev/monorepos
If the project is a monorepo, add @source directives in global.css so Tailwind scans packages outside the CSS entry file's directory (only if that directory has components with Tailwind classes):
FAQ
Docs: https://docs.uniwind.dev/faq
Custom Fonts : Uniwind maps className to font family only — font files must be loade