- Skill ID
- uni-stack/uniwind/migrate-nativewind-to-uniwind
- Publisher
- uni-stack
- Repository
- uniwind
- Installs
- 0
- Files
- 1
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
uni-stack/uniwind/migrate-nativewind-to-uniwindInstalls these files- SKILL.md
What this skill tells the agent
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.tsmetro.config.jsbabel.config.jsglobal.cssor equivalent CSS entry filenativewind-env.d.tsornativewind.d.ts- Any file using
cssInteroporremapPropsfromnativewind - 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):
npm uninstall nativewind react-native-css-interop
# or
yarn remove nativewind react-native-css-interop
# or
bun remove nativewind react-native-css-interopCRITICAL: 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:
rg "react-native-css-interop" -g "*.{ts,tsx,js,jsx}"Remove every import and usage found.
Step 2: Install Uniwind and Tailwind 4
npm install uniwind tailwindcss@latest
# or
yarn add uniwind tailwindcss@latest
# or
bun add uniwind tailwindcss@latestEnsure tailwindcss is version 4+.
Step 3: Update babel.config.js
Remove the NativeWind babel preset:
// 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):
const { withNativeWind } = require('nativewind/metro');
module.exports = withNativeWind(config, { input: './global.css' });After (Uniwind):
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.
// ❌ 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:
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 to14to match NativeWind's rem baseextraThemes(required if project has custom themes): array of custom theme names — do NOT includelight/darkdtsFile(optional): path for generated TypeScript types, defaults to./uniwind-types.d.tsdebug(optional): log unsupported CSS properties during dev
Step 5: Update global.css
Replace NativeWind's Tailwind 3 directives with Tailwind 4 imports:
Before:
@tailwind base;
@tailwind components;
@tailwind utilities;After:
@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):
module.exports = {
theme: {
extend: {
colors: {
primary: '#00a8ff',
secondary: '#273c75',
},
fontFamily: {
normal: ['Roboto-Regular'],
bold: ['Roboto-Bold'],
},
},
},
};After (global.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:
rg "cssInterop|remapProps" -g "*.{ts,tsx,js,jsx}"Replace every cssInterop() / remapProps() call with Uniwind's withUniwind():
Before (NativeWind):
import { cssInterop } from 'react-native-css-interop';
import { Image } from 'expo-image';
cssInterop(Image, { className: 'style' });After (Uniwind):
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:
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:// 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: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:
// 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"
More from uni-stack
Use this skill in a project
Open any RiverX project, search the Skills panel for uni-stack/uniwind/migrate-nativewind-to-uniwind, and install it in one click.
