Uniwind: Tailwind Finally Feels at Home in React Native
If you have ever wished you could just slap a className="flex-1 bg-white p-4" onto a React Native View and have it work, Uniwind is here to grant that wish. Created by the team behind Unistyles, one of the most respected styling libraries in the React Native ecosystem, Uniwind delivers Tailwind CSS utility classes directly to your native components with styles computed at build time rather than runtime. The result is a developer experience that feels like writing Tailwind for the web, but with the performance characteristics mobile apps demand.
What Makes Uniwind Stand Out
Uniwind is not the first attempt at bringing Tailwind to React Native, but it takes a fundamentally different approach. Instead of interpreting class names at runtime, Uniwind processes them during the build step through a Metro transformer or Vite plugin. This means your styles are already resolved by the time your app launches, eliminating the overhead that runtime solutions introduce.
Beyond raw performance, Uniwind ships with a thoughtful feature set: dark mode with automatic system theme detection, scoped themes for applying different visual treatments to component subtrees, platform-specific variants like ios: and android:, pseudo-class support for interactive states, and safe area utilities that understand device notches and home indicators. It requires Tailwind CSS 4, embracing the latest version rather than maintaining backward compatibility with older configurations.
Getting Started
Installation is straightforward. You need both Uniwind and Tailwind CSS 4:
// Using npm
npm install uniwind tailwindcss
// Using yarn
yarn add uniwind tailwindcss
If you prefer starting from a template, Uniwind offers ready-made setups:
npx create-expo-app -e with-router-uniwind
Wiring Up Your Bundler
After installing, create a global.css file that imports both Tailwind and Uniwind:
@import 'tailwindcss';
@import 'uniwind';
Then configure your Metro bundler to use the Uniwind transformer:
const { withUniwindConfig } = require('uniwind/metro');
const config = getDefaultConfig(__dirname);
module.exports = withUniwindConfig(config, {
cssEntryFile: './src/global.css',
dtsFile: './src/uniwind-types.d.ts',
});
The dtsFile option generates TypeScript definitions so your editor can autocomplete class names. If you are using Vite for React Native Web, the setup is even simpler:
import { uniwind } from 'uniwind/vite';
export default defineConfig({
plugins: [uniwind()],
});
Your First Styled Components
Laying Out a Screen
Every React Native component gets a className prop automatically. No wrappers, no higher-order components, no special imports beyond your usual React Native ones:
import { View, Text, Image } from 'react-native';
export function ProfileCard() {
return (
<View className="flex-1 bg-white dark:bg-gray-900 p-4">
<Image
className="w-24 h-24 rounded-full self-center"
source={{ uri: 'https://example.com/avatar.png' }}
/>
<Text className="text-xl font-bold text-gray-900 dark:text-white text-center mt-4">
Jane Developer
</Text>
<Text className="text-sm text-gray-500 dark:text-gray-400 text-center">
React Native Engineer
</Text>
</View>
);
}
Notice the dark: prefixes. Uniwind detects the system color scheme automatically when you use the built-in system theme, and those variants activate without any additional configuration.
Handling Interactive States
Uniwind supports pseudo-class variants for interactive components. The focus:, active:, and disabled: prefixes work on Pressable and other touchable elements:
import { Pressable, Text } from 'react-native';
export function ActionButton({ label, onPress, disabled }: {
label: string;
onPress: () => void;
disabled?: boolean;
}) {
return (
<Pressable
className="bg-blue-600 active:bg-blue-700 disabled:bg-gray-300 rounded-lg py-3 px-6"
onPress={onPress}
disabled={disabled}
>
<Text className="text-white font-semibold text-center disabled:text-gray-500">
{label}
</Text>
</Pressable>
);
}
Platform-Specific Styling
One of Uniwind's unique features is platform variants. You can target specific platforms directly in your class names:
import { View, Text } from 'react-native';
export function PlatformHeader() {
return (
<View className="p-4 ios:pt-14 android:pt-8 bg-white dark:bg-gray-900">
<Text className="text-lg font-bold ios:font-medium android:font-bold">
My App
</Text>
</View>
);
}
The ios:, android:, web:, tv:, android-tv:, and apple-tv: prefixes let you fine-tune styles per platform without conditional logic in your component code.
Going Deeper
Theme Orchestration
Uniwind ships with three built-in themes: light, dark, and system. The system theme automatically follows the device color scheme, while setting light or dark explicitly locks the app to that mode and updates native system dialogs to match.
You can switch themes at runtime through the global Uniwind object:
import { Uniwind } from 'uniwind';
function toggleTheme() {
const current = Uniwind.currentTheme;
if (current === 'light') {
Uniwind.setTheme('dark');
} else {
Uniwind.setTheme('light');
}
}
For custom themes, define CSS variables in your Tailwind configuration and switch between them. The useUniwind hook lets you reactively access the current theme state:
import { useUniwind } from 'uniwind';
function ThemeIndicator() {
const { theme, hasAdaptiveThemes } = useUniwind();
return (
<Text className="text-sm text-gray-600 dark:text-gray-400">
Current theme: {theme} {hasAdaptiveThemes ? '(auto)' : '(manual)'}
</Text>
);
}
Scoped Themes for Component Subtrees
Since version 1.4.0, Uniwind supports scoped themes. This lets you apply a different theme to a specific portion of your component tree without affecting the rest of the app. Think of a settings panel that previews what dark mode looks like while the rest of the app stays in light mode, or a card component that always renders in a branded color scheme regardless of the global theme.
Wrapping Third-Party Components
Not every component in your app will be from React Native core. The withUniwind higher-order component adds className support to any third-party component that accepts a style prop:
import { withUniwind } from 'uniwind';
import { LinearGradient } from 'expo-linear-gradient';
const StyledGradient = withUniwind(LinearGradient);
function GradientCard() {
return (
<StyledGradient className="flex-1 rounded-2xl p-6">
<Text className="text-white text-lg font-bold">
Styled with Uniwind
</Text>
</StyledGradient>
);
}
Safe Area Awareness
Mobile devices have notches, dynamic islands, and home indicators that eat into your layout. Uniwind provides safe area utilities that handle this elegantly:
import { View, Text } from 'react-native';
export function SafeScreen() {
return (
<View className="flex-1 bg-white p-safe">
<Text className="text-lg">
This content respects device safe areas on all sides.
</Text>
</View>
);
}
The p-safe, m-safe, and inset-safe utilities automatically account for device-specific insets without importing SafeAreaView or managing inset values manually.
Free vs Pro: Choosing Your Path
Uniwind follows a freemium model. The free version covers the vast majority of use cases and is what most projects will need. The Pro version, built on the proven Unistyles C++ engine, adds zero-rerender updates through Shadow Tree manipulation, Reanimated 4 animation support via class names, automatic font scaling, and native-layer safe area insets. If your app has demanding performance requirements or complex animation needs, the Pro version is worth evaluating. For most projects, the free version delivers an excellent experience.
A Few Things to Keep in Mind
React Native uses the Yoga layout engine, which means flexbox is the default layout model with a column direction. Some web-specific Tailwind utilities are not available: hover: and visited: pseudo-classes, CSS Grid, float-based layouts, and pseudo-elements like before: and after:. These are fundamental platform differences rather than Uniwind limitations. The library supports the full range of utilities that make sense in a native context, including layout, spacing, typography, colors, borders, transforms, and positioning.
Wrapping Up
Uniwind brings a mature, performance-first approach to Tailwind styling in React Native. The build-time computation strategy, combined with thoughtful features like scoped themes, platform variants, and safe area utilities, makes it a compelling choice for teams that want the productivity of utility-first CSS in their mobile apps. Backed by the Unistyles team's deep experience with React Native internals, it has quickly gained traction with over 1,300 GitHub stars and 76,000 weekly downloads in its first eight months. Whether you are starting a new React Native project or migrating from another styling solution, Uniwind deserves a serious look.