Skip to main content
Migrating your project from Nativewind to Uniwind should take no more than a few minutes. This guide highlights the main differences and provides a step-by-step migration process.

Key Differences

Before starting the migration, it’s important to understand how Uniwind differs from Nativewind:
Uniwind supports Tailwind 4 only. Make sure you upgrade Tailwind CSS to version 4.
  • Default rem value: Uniwind uses 16px as the default value for the rem unit
  • No global overrides: We don’t override global components like Nativewind’s cssInterop
  • CSS-based theming: Themes are defined in CSS files instead of tailwind.config.js
  • No ThemeProvider required: Uniwind doesn’t require wrapping your app in a ThemeProvider to switch themes

Prerequisites

Nativewind depends on the following packages:
  • react-native-reanimated
  • react-native-safe-area-context
  • tailwindcss
We recommend keeping both react-native-reanimated and react-native-safe-area-context in your project, as they are very useful for React Native development.
You’ll need to upgrade tailwindcss to version 4, as Uniwind requires it.

Migration Steps

Step 1: Install Uniwind

Follow the Quickstart guide to install Uniwind in your project.

Step 2: Remove Nativewind Babel preset

Remove the Nativewind Babel preset from your babel.config.js file:
babel.config.js
module.exports = {
  presets: ['<existing presets>'], 
  presets: ['<existing presets>', 'nativewind/babel'], 
};

Step 3: Update Metro configuration

Modify your metro.config.js to remove the Nativewind configuration and use Uniwind’s configuration instead:
metro.config.js
const { getDefaultConfig } = require('@react-native/metro-config');
const { withUniwindConfig } = require('uniwind/metro'); 

const config = getDefaultConfig(__dirname);

module.exports = withUniwindConfig(config, { 
  cssEntryFile: './src/global.css'
});
Learn more about Metro configuration in the Metro Config documentation.

Step 4: Update your global CSS file

Replace the top of your global.css file with the following imports:
global.css
@import 'tailwindcss';
@import 'uniwind';

/* Your custom CSS and theme configuration (see Step 6) */

Step 5: Remove Nativewind type definitions

Delete the nativewind.d.ts file from your project, as it’s no longer needed.

Step 6: Convert your CSS to Tailwind 4 syntax

You can keep most of your existing CSS as-is, but you’ll need to follow Tailwind 4’s @theme syntax for theme configuration.
Check out the official Tailwind 4 theme guide for more details on the new syntax.

Step 7: Migrate theme variables from JavaScript to CSS

If you defined custom theme variables using Nativewind’s vars helper:

Before: JavaScript theme configuration

vars.ts
import { vars } from 'nativewind'

export const themes = {
  light: vars({
    '--color-primary': '#00a8ff',
    '--color-gray': '#f0f0f0',
    '--color-typography': '#000',
  }),
  dark: vars({
    '--color-primary': '#273c75',
    '--color-gray': '#353b48',
    '--color-typography': '#fff',
  }),
}

After: CSS theme configuration

Move these variables directly to your global.css file:
global.css
@import 'tailwindcss';
@import 'uniwind';

/* Other directives like @theme or custom CSS */

@layer theme {
  :root {
    @variant light {
      --color-primary: #00a8ff;
      --color-gray: #f0f0f0;
      --color-typography: #000;
    }

    @variant dark {
      --color-primary: #273c75;
      --color-gray: #353b48;
      --color-typography: #fff;
    }
  }
}
You can now safely delete the file containing the vars helper, as it’s no longer used.
If you need to access CSS variables in JavaScript, you can use useResolveClassNames hook.

Step 8: Remove tailwind.config.js

With Uniwind, you no longer need a tailwind.config.js file. Theme configuration is now done entirely in CSS.
tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./App.tsx'],
  presets: [require('nativewind/preset')],
  theme: {
    extend: {
      colors: {
        primary: 'var(--color-primary)',
        gray: 'var(--color-gray)',
        typography: 'var(--color-typography)',
      },
    },
  },
  plugins: [],
}
Delete this file. All theme configuration should now be in your global.css file.

Step 9: (Optional) Customize the default rem value

If you want to keep Nativewind’s default rem value of 14px, configure it in your metro.config.js:
metro.config.js
module.exports = withUniwindConfig(config, {
  cssEntryFile: './src/global.css',
  polyfills: { 
    rem: 14,
  },
});

Step 10: Remove ThemeContext where you set colorScheme

Uniwind doesn’t require a ThemeProvider to manage themes. Simply remove it from your app:
ThemeProvider.tsx
export const ThemeProvider = ({ children }: ThemeProviderProps) => {
    const { colorScheme } = useColorScheme()

    return (
        <ThemeContext.Provider value={{ theme: colorScheme as 'light' | 'dark' }}>
            <View style={themes[colorScheme as 'light' | 'dark']} className="flex-1">
                {children}
            </View>
        </ThemeContext.Provider>
    )
}
App.tsx
import { ThemeProvider } from './ThemeProvider';

export default function App() {
  return (
    <ThemeProvider>
      <YourApp />
    </ThemeProvider>
  );
}
App.tsx
export default function App() {
  return <YourApp />;
}

Step 11: Replace cssInterop with withUniwind

If you’re using Nativewind’s cssInterop to style third-party components, replace it with Uniwind’s withUniwind:
Learn more about withUniwind in the withUniwind API documentation.

Step 12: Handle safe area utilities

If you used Nativewind’s p-safe or m-safe class names, you’ll need to use the useSafeAreaInsets hook from react-native-safe-area-context instead:
export default function App() {
  return <View className="p-safe" />;
}
import { useSafeAreaInsets } from 'react-native-safe-area-context';

export default function App() {
  const insets = useSafeAreaInsets();
  return <View style={{ paddingTop: insets.top }} />;
}
Uniwind doesn’t depend on react-native-safe-area-context, and there’s no way to access safe area values directly from React Native’s core APIs.
You can also use SafeAreaView from react-native-safe-area-context to handle safe area insets.

Step 13: Update animated class names

If you used Nativewind’s animated class names from Tailwind, you’ll need to use Reanimated’s syntax directly.
Check out the React Native Reanimated documentation for animation patterns.

Need Help?

Missing Features?

If you’re using Nativewind and notice any missing features in Uniwind, please open an issue on GitHub. We’re happy to add support!
Still having issues with migration? Start a discussion on GitHub and we’ll help you migrate.