Common Questions
How do I include custom fonts?
How do I include custom fonts?
className props to font families, but the actual font files need to be included separately.Expo Projects
Step 1: Install and configure expo-font
Add the font files to your project and configure them inapp.json:Step 2: Define font families in global.css
Configure your font families and text sizes using CSS variables in the@theme directive:Step 3: Use font classes in your components
Now you can use the configured font families with Tailwind classes:Bare React Native Projects
For bare React Native projects without Expo, you can include fonts using thereact-native.config.js file:Step 1: Create react-native.config.js
Step 2: Link the fonts
Run the following command to link your fonts:Step 3: Configure in global.css
After linking the fonts, configure them in yourglobal.css the same way as Expo projects:Platform-Specific Fonts
You can define different fonts for different platforms using@variant:Troubleshooting
Fonts not loading
If your fonts aren’t appearing:- Check font file names - Make sure the font family name in CSS matches the font file name exactly
- Rebuild the app - Font changes require a full rebuild, not just a Metro refresh
- Verify file paths - Ensure the paths in
app.jsonorreact-native.config.jsare correct - Clear cache - Try clearing Metro bundler cache with
npx expo start --clear
Font looks different than expected
React Native doesn’t support dynamic font weights. Each weight requires its own font file. Make sure you’ve:- Included all the font weight variants you need
- Mapped each variant to a CSS variable in
@theme - Used the correct className for each weight
Platform Selectors
How can I style components based on prop values (data selectors)?
How can I style components based on prop values (data selectors)?
data-[...] variant syntax to apply styles conditionally based on your own data-* props.Basic usage
- Works with booleans:
data-[selected=true]:ring-2(prop:data-selected) - Works with strings:
data-[variant=primary]:bg-primary(prop:data-variant)
Notes
- Only equality checks are supported (e.g.,
data-[prop=value]) - Define and pass your own data-* props on React Native components
Data Selectors
Where should I put global.css in Expo Router?
Where should I put global.css in Expo Router?
global.css file in the project root and import it in your root layout file.Recommended Setup
Step 1: Create global.css in the project root
Place yourglobal.css file in the root of your project:Step 2: Import in your root layout
Import the CSS file in your root layout file (app/_layout.tsx):_layout.tsx ensures the CSS is loaded before any of your app screens render, and enables hot reload when you modify styles.Step 3: Configure Metro
Point Metro to your CSS file:Why This Structure?
- No @source needed: Tailwind scans from the project root, so it automatically finds
appandcomponentsdirectories - Simpler setup: No need to manually configure which directories to scan
- Standard convention: Matches typical React Native project structure
Alternative: App Directory
You can also placeglobal.css inside the app directory:_layout.tsx:Global CSS Location Guide
Monorepos & @source
Why does my app still fully reload when I change CSS?
Why does my app still fully reload when I change CSS?
global.css in your root index file, the issue is likely caused by too many Providers in your main App component.The Problem
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.Common scenario:
global.css triggers a full app reload.The Solution
Move theglobal.css import one level deeper to a component that has fewer providers:Option 1: Import in your navigation root
Option 2: Import in your home/main screen
Option 3: Import in Expo Router’s nested layout
If using Expo Router, move the import to a nested layout:How to Choose Where to Import
Importglobal.css in the deepest component that:- ✅ Is mounted early in your app lifecycle
- ✅ Doesn’t have many providers or complex state
- ✅ Is a good candidate for Fast Refresh
- ✅ Runs on all platforms (iOS, Android, Web)
Testing the Fix
After moving the import:- 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
Why This Happens
Metro’s Fast Refresh works by:- Detecting which files changed
- Finding components that can be safely updated
- Hot swapping only those components
Fast Refresh Documentation
How can I use gradients?
How can I use gradients?
Built-in Gradient Support (Recommended)
Use gradient classes directly with theclassName prop:Directional Gradients
bg-gradient-to-t- Topbg-gradient-to-r- Rightbg-gradient-to-b- Bottombg-gradient-to-l- Leftbg-gradient-to-tr- Top rightbg-gradient-to-br- Bottom rightbg-gradient-to-bl- Bottom leftbg-gradient-to-tl- Top left
Angle-based Gradients
Use specific angles withbg-linear-{angle}:Multi-stop Gradients
Usefrom-, via-, and to- for multiple color stops:Custom Gradients with Arbitrary Values
For complete control, use arbitrary values with custom angles and color stops:bg-linear-[angle,color1_position,color2_position,...]Using expo-linear-gradient
If you need to useexpo-linear-gradient for specific features, you can’t use withUniwind since it doesn’t support mapping props to arrays. Instead, use multiple useCSSVariable calls:❌ This won’t work
✅ Use useCSSVariable instead
Examples
Card with Gradient Background
Button with Gradient
Theme-aware Gradient
How do you handle merging and deduplicating classNames?
How do you handle merging and deduplicating classNames?
className="bg-red-500 bg-blue-500", both classes will be applied, and the behavior depends on CSS specificity rules.Using tailwind-merge (Recommended)
For proper className merging and deduplication, we recommend usingtailwind-merge with a utility function:Step 1: Install dependencies
- bun
- npm
- yarn
Step 2: Create a cn utility
Create a utility file (e.g.,lib/utils.ts or utils/cn.ts):Step 3: Use the cn utility
Now you can merge classNames safely in your components:Why Use tailwind-merge?
Withouttailwind-merge, conflicting classes can cause issues:❌ Without tailwind-merge
✅ With tailwind-merge
Conditional Class Merging
Theclsx library inside cn makes conditional classes easier:How does style specificity work in Uniwind?
How does style specificity work in Uniwind?
Inline styles override className
In Uniwind inline styles always have higher priority than className:Platform-specific behavior
Specificity rules work consistently across platforms:Best practices
How to debug 'Failed to serialize javascript object' error?
How to debug 'Failed to serialize javascript object' error?
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.< 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.The Error
global.css file. It can cause your app to fail to build or display a white screen.@theme configurations, custom properties, or advanced CSS features), not about using className in your components.Quick fix: clear Metro cache first
In some cases, this error can be caused by stale Metro or bundler cache. Before patchingnode_modules, run:- Watchman file watcher cache
node_modules/.cache(Babel/bundler caches)- Expo’s internal Metro cache (
--clear)
Debugging Steps
To identify what’s causing the serialization issue, follow these steps:Step 1: Add debug logging
Navigate to the Uniwind Metro transformer file and add a console log to see what’s failing:Step 2: Run your app
After adding the console log, run your Metro bundler:Step 3: Check the output
Look at your Metro terminal output. You should see which object or code pattern is causing the serialization failure.Step 4: Report the issue
Once you’ve identified the problematic code:- Copy the console.log output
- Create a minimal reproduction case if possible
- Report it on GitHub with the output
Common Causes in global.css
This error is caused by complex patterns in yourglobal.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
How can I fix Metro unstable_enablePackageExports conflicts?
How can I fix Metro unstable_enablePackageExports conflicts?
unstable_enablePackageExports in their Metro configuration. However, Uniwind requires this setting to be enabled to work properly.The Problem
If your Metro config has:culori) won’t work correctly because they require package exports to be enabled.The Solution
You can selectively enable package exports only for Uniwind and its dependencies while keeping it disabled for everything else:Why This Works
The customresolveRequest function:- Checks the module name - If it’s
uniwindorculori, it enables package exports - Creates a new context - Temporarily overrides the setting for these specific packages
- Falls back to default - All other packages use the global setting (
false)
When You Need This
Use this solution if:- 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
unstable_enablePackageExports, you don’t need this custom resolver. Uniwind works fine with the default Metro configuration.Troubleshooting
If you still encounter issues after adding the custom resolver:- Clear Metro cache - Run
npx expo start --clearornpx react-native start --reset-cache - Rebuild the app - Package export changes may require a full rebuild
- Check the module name - Ensure the module causing issues is included in the
['uniwind', 'culori']array - Verify Metro config - Make sure the custom resolver is defined before calling
withUniwindConfig
Metro Configuration
Does Uniwind work with Vite?
Does Uniwind work with Vite?
Setup
Createvite.config.ts in your project root:cssEntryFile to the CSS file where you import tailwindcss and uniwind. Keep it at your app root for accurate class scanning.How do I enable safe area classNames?
How do I enable safe area classNames?
react-native-safe-area-context and wire safe area insets to Uniwind.Setup
- Add the dependency:
- bun
- npm
- yarn
- Wrap your root layout with
SafeAreaListenerand forward insets to Uniwind:
Available classNames
Uniwind provides three categories of safe area utilities:- Padding:
p-safe,pt-safe,pb-safe,pl-safe,pr-safe,px-safe,py-safe - Margin:
m-safe,mt-safe,mb-safe,ml-safe,mr-safe,mx-safe,my-safe - Inset (positioning):
inset-safe,top-safe,bottom-safe,left-safe,right-safe,x-safe,y-safe
or and offset variants:{property}-safe-or-{value}→Math.max(inset, value)- ensures minimum spacing (e.g.,pt-safe-or-4){property}-safe-offset-{value}→inset + value- adds extra spacing on top of inset (e.g.,mb-safe-offset-2)
Class matrix
| Class | Example | Effect |
|---|---|---|
p-safe | className="p-safe" | Sets all padding to the current inset values |
pt-safe | className="pt-safe" | Top padding equals top inset |
m-safe | className="m-safe" | Sets all margins to the inset values |
inset-safe | className="inset-safe" | Sets top/bottom/left/right position to inset values |
top-safe | className="top-safe" | Top position equals top inset |
y-safe | className="y-safe" | Top and bottom positions equal their insets |
pt-safe-or-4 | className="pt-safe-or-4" | Top padding is Math.max(topInset, 16) |
pb-safe-offset-4 | className="pb-safe-offset-4" | Bottom padding is bottomInset + 16 |
top-safe-offset-4 | className="top-safe-offset-4" | Top position is topInset + 16 |
Positioning Examples
Use inset utilities for absolutely positioned elements that need to respect safe areas:Does Uniwind work with Next.js?
Does Uniwind work with Next.js?
Current Support
Uniwind works out of the box with:- ✅ React Native (Bare workflow)
- ✅ Expo (Managed and bare workflows)
- ✅ Metro bundler (React Native’s default bundler)
- ✅ Vite (with
vite-plugin-rnwanduniwind/vitefor web)
Why Not Next.js?
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.Community Solution
@a16n-dev has createduniwind-plugin-next, a webpack plugin that integrates Uniwind into Next.js applications with SSR support.GitHub Repository
Live Demo
Official Next.js Support
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.Alternatives for Cross-Platform
If the community plugin doesn’t fit your needs:- 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
What UI kits work well with Uniwind?
What UI kits work well with Uniwind?
Recommended UI Kits
React Native Reusables
HeroUI Native
React Native Reusables - shadcn for React Native
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
HeroUI Native - Complete Component Library
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
className prop and support all Tailwind utilities out of the box. Choose based on your preferred workflow: copy-paste (Reusables) or npm install (HeroUI).More UI Kits Coming
We’re actively working with other UI library teams to bring first-class Uniwind support to more component libraries. Stay tuned for announcements!Using Other Component Libraries
Uniwind works with any React Native component library. For libraries that don’t natively supportclassName, you can use withUniwind to add className support: