Common issues and solutions for Uniwind — the React Native Tailwind CSS library. Setup errors, theming questions, className merging, migration from NativeWind, and performance tips.
Custom fonts require two steps: loading the font files into your app and configuring the font names in your CSS. Uniwind maps className props to font families, but the actual font files need to be included separately.
Important: Uniwind only handles the mapping of classNames to font families. You must include and load the font files separately using Expo Font or React Native’s asset system.
Place your font files in the assets/fonts directory or any directory structure that works for your project. Just make sure the paths in app.json match your actual file locations.
The font family names in your CSS must exactly match the font file names (without the extension). For example, Roboto-Regular.ttf becomes 'Roboto-Regular'.
Now you can use the configured font families with Tailwind classes:
import { Text } from 'react-native'export const CustomFontExample = () => ( <> <Text className="font-sans text-base"> Regular text using Roboto-Regular </Text> <Text className="font-sans-medium text-lg"> Medium weight using Roboto-Medium </Text> <Text className="font-sans-bold text-xl"> Bold text using Roboto-Bold </Text> <Text className="font-mono text-sm"> Monospace text using FiraCode-Regular </Text> </>)
The location of global.css determines your app root. Tailwind will only scan for classNames starting from that directory.
Global CSS Location Guide
Learn more about configuring global.css
Monorepos & @source
Understand how @source works with multiple directories
Why does my app still fully reload when I change CSS?
If you’re experiencing full app reloads when modifying CSS, even though you followed the documentation and didn’t import global.css in your root index file, the issue is likely caused by too many Providers in your main App component.
Metro’s Fast Refresh can’t hot reload components that have too many context providers, state management wrappers, or complex component trees. This is a Metro limitation, not a Uniwind issue.
The goal is to find a component that Metro can efficiently hot reload. Experiment with different locations until you find one that enables Fast Refresh for CSS changes.
Restart Metro - Clear cache with npx expo start --clear
Make a CSS change - Modify a color in global.css
Check for Fast Refresh - Your app should update without a full reload
If you still see full reloads, try moving the import one level deeper. Some apps with very complex structures may need the import quite deep in the component tree.
Built-in gradients work seamlessly with theme colors and support all Tailwind color utilities like from-blue-500, via-purple-600, etc.You can check more examples in the offical Tailwind CSS documentation.
If you need to use expo-linear-gradient for specific features, you can’t use withUniwind since it doesn’t support mapping props to arrays. Instead, use multiple useCSSVariable calls:
For most use cases, we recommend using built-in gradient support instead of expo-linear-gradient. It’s simpler, requires no extra dependencies, and integrates better with Tailwind syntax.
import { View, Text } from 'react-native'export const ThemedGradient = () => ( <View className="bg-gradient-to-r from-blue-500 to-purple-500 dark:from-blue-700 dark:to-purple-700 p-6 rounded-xl"> <Text className="text-white text-lg"> This gradient adapts to the theme </Text> </View>)
How do you handle merging and deduplicating classNames?
Uniwind does not automatically deduplicate classNames, especially on web. When you have conflicting styles or duplicate classes, you’ll need to handle merging manually.
Important: Uniwind doesn’t dedupe classNames. If you pass conflicting styles like className="bg-red-500 bg-blue-500", both classes will be applied, and the behavior depends on CSS specificity rules.
Create a utility file (e.g., lib/utils.ts or utils/cn.ts):
lib/utils.ts
import { type ClassValue, clsx } from 'clsx';import { twMerge } from 'tailwind-merge';export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs));}
import { View } from 'react-native'const baseClasses = 'bg-red-500 p-4'const customClasses = 'bg-blue-500'// Both bg-red-500 and bg-blue-500 are applied// Result is unpredictable<View className={`${baseClasses} ${customClasses}`} />
Understanding style specificity and priority is important when working with Uniwind to ensure predictable styling behavior.Inline styles always have higher priority than className styles, even when the class uses Tailwind’s important modifier (!).
import { View } from 'react-native'<View className="!bg-red-500" style={{ backgroundColor: 'blue' }} // This wins/>
Result: The background will be blue.Read the full Style Specificity guide for className conflicts, important utilities like !bg-red-500, inline styles, and best practices.
How to debug 'Failed to serialize javascript object' error?
Starting with 1.0.0-rc.6, Uniwind automatically filters out unserializable tokens instead of failing the entire build. Invalid rules are logged as warnings in your Metro terminal and gracefully skipped, so your app continues to work. If you see these warnings, report the affected CSS pattern on GitHub so it can be properly supported.
Uniwind Error - Failed to serialize { %op: true } as a valid JS object entry. Skipping.
On older versions (< 1.0.0-rc.6), this error would crash the build. If you’re on an older version, upgrade to get automatic filtering, or follow the debugging steps below.
If you encounter the error “Uniwind Error - Failed to serialize javascript object” on an older version, this means Uniwind’s Metro transformer is unable to serialize a complex pattern in your global.css file. This error is specifically about CSS processing, not about classNames in your components.
Uniwind Error - Failed to serialize javascript object
This error appears during the Metro bundling process when Uniwind tries to process your global.css file. It can cause your app to fail to build or display a white screen.
This error is about CSS patterns in global.css (like complex @theme configurations, custom properties, or advanced CSS features), not about using className in your components.
// Find the serializeJSObject function and update the catch block:try { new Function(`function validateJS() { const obj = ({ ${serializedObject} }) }`);} catch { // Add this console.log to see what's failing console.log('Serialization failed for:', serializedObject); Logger.error("Failed to serialize javascript object"); return "";}return serializedObject;
This error is caused by complex patterns in your global.css file that the Metro transformer can’t serialize. Common causes include:
Complex @theme configurations - Very large or deeply nested theme definitions
Advanced CSS functions - Custom CSS functions or calculations that use JavaScript-like syntax
Non-standard CSS syntax - Experimental or non-standard CSS features
Circular references - CSS variables that reference each other in complex ways
Report Serialization Issues
Found a serialization issue? Help improve Uniwind by reporting it
How can I fix Metro unstable_enablePackageExports conflicts?
Some React Native apps (especially crypto apps) need to disable unstable_enablePackageExports in their Metro configuration. However, Uniwind requires this setting to be enabled to work properly.
You’re working with crypto libraries that break with package exports enabled
You have other dependencies that require unstable_enablePackageExports = false
You encounter module resolution errors with Uniwind after disabling package exports
If you don’t have any conflicts with unstable_enablePackageExports, you don’t need this custom resolver. Uniwind works fine with the default Metro configuration.
import tailwindcss from '@tailwindcss/vite'import { uniwind } from 'uniwind/vite'import { defineConfig } from 'vite'import { rnw } from 'vite-plugin-rnw'export default defineConfig({ plugins: [ rnw(), tailwindcss(), uniwind({ // support same configuration as Metro plugin cssEntryFile: './src/App.css', extraThemes: ['premium'], }), ],})
Point cssEntryFile to the CSS file where you import tailwindcss and uniwind. Keep it at your app root for accurate class scanning.
Restart Vite after changing your CSS entry or adding new themes.
Use inset utilities for absolutely positioned elements that need to respect safe areas:
// Floating action button above the bottom safe area<Pressable className="absolute bottom-safe right-4 bg-blue-500 rounded-full p-4"> <PlusIcon /></Pressable>// Full-screen overlay respecting all safe areas<View className="absolute inset-safe bg-black/50"> {/* Modal content */}</View>// Header positioned below top safe area with extra padding<View className="absolute top-safe-offset-4 left-0 right-0"> {/* Header content */}</View>
Does Uniwind work with Next.js?
Not officially. Uniwind is built for Metro and Vite (via React Native Web), not for Next.js. However, there’s an experimental community-driven plugin that adds Next.js support.
Next.js uses Webpack (or Turbopack) as its bundler, while Uniwind is architected around Metro’s transformer pipeline. These are fundamentally different build systems with different APIs and plugin architectures.
There is currently no timeline for official Next.js support. While the community plugin works well for many use cases, official support would require significant effort to build and maintain a separate Webpack/Turbopack plugin alongside the Metro architecture.If the community plugin doesn’t meet your needs, consider the alternatives below.
Use Uniwind for React Native/Expo - For your mobile apps
Use standard Tailwind CSS for Next.js - For your web app
Share design tokens - Keep your color palette and spacing consistent via shared configuration
Many teams successfully use Uniwind for their React Native apps while using standard Tailwind CSS for their Next.js web apps, sharing design tokens between them.
What UI kits work well with Uniwind?
Uniwind works with any React Native component library, but we’ve worked closely with UI kit teams to ensure the best integration and performance.
React Native Reusables brings the beloved shadcn/ui philosophy to React Native. Built with Uniwind (or NativeWind), it provides beautifully designed, accessible, and customizable components that you can copy and paste into your apps.Why React Native Reusables?
🎨 shadcn Philosophy - Copy, paste, and own your components. No package bloat
✅ Uniwind Native - Built specifically for Uniwind with full className support
🎯 Beautifully Crafted - Premium design inspired by shadcn/ui’s aesthetics
♿ Accessible - WCAG-compliant components that work across all platforms
🎛️ Fully Customizable - Modify components to match your exact design requirements
📱 React Native First - Designed for mobile, works perfectly on iOS, Android, and web
Perfect for developers who love shadcn/ui’s approach and want the same elegant components for React Native. Just copy, paste, and customize to your heart’s content!
HeroUI Native is a comprehensive, production-ready React Native UI library. It’s built for speed, accessibility, and seamless integration with Uniwind.Why HeroUI Native?
✅ Built for Uniwind - Designed to work seamlessly with Uniwind’s styling system
✅ Optimized Performance - Collaborated closely with the HeroUI team for best-in-class performance
✅ Accessible - ARIA-compliant components that work on all platforms
✅ Extensive Theming - Deep integration with Uniwind’s theme system
✅ Modern Design - Beautiful, contemporary components out of the box
✅ Comprehensive - Full set of components for building production apps
Both UI kits work seamlessly with Uniwind’s className prop and support all Tailwind utilities out of the box. Choose based on your preferred workflow: copy-paste (Reusables) or npm install (HeroUI).
We’re actively working with other UI library teams to bring first-class Uniwind support to more component libraries. Stay tuned for announcements!
Want your UI kit featured here? We collaborate closely with library authors to ensure optimal integration and performance. Reach out on GitHub Discussions!
Uniwind works with any React Native component library. For libraries that don’t natively support className, you can use withUniwind to add className support:
import { withUniwind } from 'uniwind'import { SomeComponent } from 'some-library'const StyledComponent = withUniwind(SomeComponent)<StyledComponent className="bg-blue-500 p-4" />
withUniwind API
Learn how to add className support to any component
Third-party Components
See examples of using Uniwind with various component libraries