A phone showing a styled mobile app next to a calm gray-blue cat on a desk

Uniwind: Tailwind for React Native Without the Runtime Tax

The Gray Cat
The Gray Cat
1 view

Styling React Native has always been a bit of a compromise. You can hand-write StyleSheet objects and lose all the speed and muscle memory you built up doing web work, or you can reach for a Tailwind-flavored library and pay for the convenience with extra work happening on every render. Uniwind is a styling library that tries to give you both: the familiar className ergonomics of Tailwind CSS, and styles that are resolved at build time so your app does less work while it runs.

It comes with a strong pedigree. Uniwind is built by the team behind Unistyles, the high-performance React Native styling library, and it carries the same obsession with performance into the Tailwind world. The project's own tagline is blunt about it: "the fastest Tailwind CSS library for React Native." It targets Tailwind v4, works across Expo, iOS, Android, tvOS, and Web, and ships happily inside Expo Go so you can try it without a custom development build.

Why Compile Styles Ahead of Time

The core idea behind Uniwind is that the work of turning a class string like bg-white p-4 rounded-lg into a React Native style object should happen as early as possible. Many className-based libraries parse those strings while your components render, which means the parsing cost shows up again and again as users scroll, navigate, and interact. Uniwind moves that translation into the bundler step instead.

The practical upshot is that by the time your component mounts, the heavy lifting is already done. The library's marketing puts a number on it, claiming roughly 2x the performance of NativeWind. Treat that as the project's own claim rather than an independently audited benchmark, but the architectural reasoning behind it is sound: less runtime parsing means fewer cycles spent on styling and more left for your actual app.

Here is what the everyday experience looks like once it's wired up:

import { View, Text } from 'react-native';

export const Card = () => (
  <View className="bg-white dark:bg-gray-900 p-4 rounded-2xl">
    <Text className="text-gray-900 dark:text-white text-lg font-semibold">
      Hello from Uniwind
    </Text>
  </View>
);

Every built-in React Native component accepts className out of the box, and you can extend that to third-party components too. If you have written Tailwind for the web, almost none of this needs explaining.

What You Get in the Box

Uniwind covers a generous slice of the Tailwind vocabulary while respecting the realities of React Native's layout engine. The highlights worth knowing:

  • Full Tailwind v4 support. This is a hard requirement, not a nice-to-have. Earlier Tailwind versions are not supported.
  • Theming with light, dark, and system modes. Adaptive theming is on by default, so your app follows the device color scheme unless you tell it otherwise.
  • State selectors like active:, disabled:, and focus: for interactive styling.
  • Platform variants including ios:, android:, web:, tv:, android-tv:, and apple-tv: so you can branch styles per platform inline.
  • Direction variants rtl: and ltr:, backed by a dedicated layout-direction component added in recent releases.
  • Safe area utilities such as p-safe, m-safe, and inset-safe for dealing with notches and home indicators.
  • Data selectors that let you style based on component props, plus an accent-* prefix for color props on native components.
  • Custom CSS properties you can use directly inside React Native, and recently added border curve handling for those smooth, continuous iOS-style rounded corners.

Because it's free and MIT licensed, the core library carries no usage restrictions for personal, commercial, or open source projects.

Getting It Running

Installation pulls in Uniwind alongside Tailwind itself.

npm install uniwind tailwindcss
yarn add uniwind tailwindcss

Next, create a global.css file at your project root. Its location matters, because Tailwind scans for class names starting from this file's directory.

@import 'tailwindcss';
@import 'uniwind';

Import that CSS from your app's main component rather than the root index.ts. Importing it at the very root triggers full reloads instead of hot reloads, which gets annoying fast.

import './global.css';

export const App = () => {
  // your app
};

Finally, wire up the bundler. For Expo or bare React Native using Metro, wrap your config with withUniwindConfig, and make sure it is the outermost wrapper around any other Metro helpers.

const { getDefaultConfig } = require('expo/metro-config');
const { withUniwindConfig } = require('uniwind/metro');

const config = getDefaultConfig(__dirname);

module.exports = withUniwindConfig(config, {
  cssEntryFile: './src/global.css',
  dtsFile: './src/uniwind-types.d.ts',
});

Use relative paths for cssEntryFile, never absolute ones. Running the Metro server generates the TypeScript typings file referenced by dtsFile, which gives you autocompletion and type safety on your class names.

Theming Without a Provider

One of the nicer surprises in Uniwind is that theming does not require wrapping your app in a ThemeProvider. It leans on CSS-first theming through Tailwind variants, so dark mode is just the dark: prefix you already know.

import { View, Text } from 'react-native';

export const Banner = () => (
  <View className="bg-white dark:bg-gray-900 p-4">
    <Text className="text-gray-900 dark:text-white">
      This adapts to the system theme automatically
    </Text>
  </View>
);

When you do need programmatic control, the useUniwind hook gives you reactive access to the current theme, and a global Uniwind object lets you switch it.

import { useUniwind, Uniwind } from 'uniwind';

const ThemeToggle = () => {
  const { theme, hasAdaptiveThemes } = useUniwind();

  return (
    <Pressable onPress={() => Uniwind.setTheme('dark')}>
      <Text>Current theme: {theme}</Text>
    </Pressable>
  );
};

// Re-enable adaptive themes that follow the device:
Uniwind.setTheme('system');

Dynamic Tokens at Runtime

Build-time compilation does not mean everything is frozen. Uniwind exposes CSS variables you can update while the app is running, which is handy for things like user-selectable accent colors or fetching brand tokens from a server.

import { Uniwind, getCSSVariable } from 'uniwind';

Uniwind.updateCSSVariables('dark', {
  '--primary-color': '#3b82f6',
  '--text-color': '#ffffff',
});

// Read a variable outside of React when you need it:
const primary = getCSSVariable('--primary-color');

This lets you ship a default theme that's resolved cheaply at build time, then tweak specific tokens on the fly without redeploying.

When You Outgrow the Free Tier

For most apps the free, MIT-licensed core is the whole story. But Uniwind also offers a commercial Pro version that pushes styling onto a native C++ engine, the same lineage that powers Unistyles. The headline Pro capabilities are aimed at apps where every frame counts:

  • Zero re-renders for a large set of props that update directly on the native thread instead of going through React's render cycle.
  • Reanimated 4 support, letting you drive animations through className rather than wiring up styles by hand.
  • Native theme transitions so dark mode and brand switches happen on the native thread.
  • Group variants where a parent's interaction state propagates through the shadow tree without context overhead.

The trade-off is that Pro needs custom development builds and does not run in Expo Go, whereas the free version happily does. That makes the free tier the obvious place to start, with Pro as an upgrade path once your performance needs justify the build complexity. Pricing lives on the Uniwind site.

Should You Use It

Uniwind is young, having shipped its 1.0 in late 2025, but it is moving fast with near-weekly releases and has already climbed to roughly 950,000 downloads a month. The open issue count sits at a tidy handful, which suggests the maintainers are keeping up with the inbound. If you are starting a new React Native project, already think in Tailwind utilities, and care about runtime performance, Uniwind makes a compelling default. The Tailwind v4 requirement and the bundler setup are the main things to plan for, but neither is heavy, and the payoff is styling that feels like the web while behaving like native code.

For teams currently on NativeWind, the migration is not free, but the architectural story, build-time compilation plus an optional native engine, is a genuinely different bet on how React Native styling should work. Given who is behind it, that bet is worth taking seriously.