Skip to main content

Common Questions

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.

Expo Projects

Step 1: Install and configure expo-font

Add the font files to your project and configure them in app.json:
app.json
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.

Step 2: Define font families in global.css

Configure your font families and text sizes using CSS variables in the @theme directive:
global.css
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'.

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 the react-native.config.js file:

Step 1: Create react-native.config.js

react-native.config.js
Run the following command to link your fonts:
This will copy your font files to the native iOS and Android projects.

Step 3: Configure in global.css

After linking the fonts, configure them in your global.css the same way as Expo projects:
global.css

Platform-Specific Fonts

You can define different fonts for different platforms using @variant:
global.css

Troubleshooting

Fonts not loading

If your fonts aren’t appearing:
  1. Check font file names - Make sure the font family name in CSS matches the font file name exactly
  2. Rebuild the app - Font changes require a full rebuild, not just a Metro refresh
  3. Verify file paths - Ensure the paths in app.json or react-native.config.js are correct
  4. 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

Learn more about using platform-specific styles
Use data selectors with Tailwind’s 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

Full documentation and examples
When using Expo Router, it’s recommended to place your global.css file in the project root and import it in your root layout file.

Step 1: Create global.css in the project root

Place your global.css file in the root of your project:
global.css

Step 2: Import in your root layout

Import the CSS file in your root layout file (app/_layout.tsx):
app/_layout.tsx
Importing in the root _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:
metro.config.js

Why This Structure?

  • No @source needed: Tailwind scans from the project root, so it automatically finds app and components directories
  • Simpler setup: No need to manually configure which directories to scan
  • Standard convention: Matches typical React Native project structure
With global.css in the root, Tailwind will automatically scan all directories (app, components, etc.) without needing @source directives.

Alternative: App Directory

You can also place global.css inside the app directory:
Then import it in _layout.tsx:
app/_layout.tsx
And update Metro config:
metro.config.js
Important: If you place global.css in the app directory and have components outside (like a components folder), you must use @source to include them:
app/global.css
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
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.

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:

App.tsx
Metro can’t efficiently hot reload this file due to the complex provider tree, so any change to global.css triggers a full app reload.

The Solution

Move the global.css import one level deeper to a component that has fewer providers:

Option 1: Import in your navigation root

App.tsx
NavigationRoot.tsx

Option 2: Import in your home/main screen

screens/HomeScreen.tsx

Option 3: Import in Expo Router’s nested layout

If using Expo Router, move the import to a nested layout:
app/_layout.tsx
app/(tabs)/_layout.tsx

How to Choose Where to Import

Import global.css in the deepest component that:
  1. ✅ Is mounted early in your app lifecycle
  2. ✅ Doesn’t have many providers or complex state
  3. ✅ Is a good candidate for Fast Refresh
  4. ✅ Runs on all platforms (iOS, Android, Web)
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.

Testing the Fix

After moving the import:
  1. Restart Metro - Clear cache with npx expo start --clear
  2. Make a CSS change - Modify a color in global.css
  3. 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.

Why This Happens

Metro’s Fast Refresh works by:
  1. Detecting which files changed
  2. Finding components that can be safely updated
  3. Hot swapping only those components
When a file has too many providers or complex state management, Metro can’t determine what’s safe to update, so it triggers a full reload instead.
This is a Metro/React Native limitation, not specific to Uniwind. Any file with complex provider trees will have this issue with Fast Refresh.

Fast Refresh Documentation

Learn more about React Native’s Fast Refresh system
Uniwind provides built-in gradient support using Tailwind syntax with React Native’s internal implementation. No additional dependencies required!Use gradient classes directly with the className prop:

Directional Gradients

Available directions:
  • bg-gradient-to-t - Top
  • bg-gradient-to-r - Right
  • bg-gradient-to-b - Bottom
  • bg-gradient-to-l - Left
  • bg-gradient-to-tr - Top right
  • bg-gradient-to-br - Bottom right
  • bg-gradient-to-bl - Bottom left
  • bg-gradient-to-tl - Top left

Angle-based Gradients

Use specific angles with bg-linear-{angle}:

Multi-stop Gradients

Use from-, via-, and to- for multiple color stops:

Custom Gradients with Arbitrary Values

For complete control, use arbitrary values with custom angles and color stops:
Syntax: bg-linear-[angle,color1_position,color2_position,...]
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.

Using expo-linear-gradient

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:

❌ This won’t work

✅ Use useCSSVariable instead

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.

Examples

Card with Gradient Background

Button with Gradient

Theme-aware Gradient

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.
For proper className merging and deduplication, we recommend using tailwind-merge with a utility function:

Step 1: Install dependencies

Step 2: Create a cn utility

Create a utility file (e.g., lib/utils.ts or utils/cn.ts):
lib/utils.ts

Step 3: Use the cn utility

Now you can merge classNames safely in your components:

Why Use tailwind-merge?

Without tailwind-merge, conflicting classes can cause issues:

❌ Without tailwind-merge

✅ With tailwind-merge

Conditional Class Merging

The clsx library inside cn makes conditional classes easier:
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 (!).
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.
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.
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.

The Error

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.

Quick fix: clear Metro cache first

In some cases, this error can be caused by stale Metro or bundler cache. Before patching node_modules, run:
This clears:
  • Watchman file watcher cache
  • node_modules/.cache (Babel/bundler caches)
  • Expo’s internal Metro cache (--clear)
If you’re using React Native CLI (not Expo), run:
If clearing the cache doesn’t resolve the issue, continue with the debugging steps below to identify the exact pattern causing serialization to fail.

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:
node_modules/uniwind/dist/metro/metro-transformer.cjs

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:
  1. Copy the console.log output
  2. Create a minimal reproduction case if possible
  3. Report it on GitHub with the output
Include the serialization output and the code pattern causing the issue. This helps the maintainers fix the serializer to support your use case.

Common Causes in global.css

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
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.

The Problem

If your Metro config has:
metro.config.js
Uniwind and its dependency (culori) won’t work correctly because they require package exports to be enabled.
Completely disabling unstable_enablePackageExports will break Uniwind’s module resolution.

The Solution

You can selectively enable package exports only for Uniwind and its dependencies while keeping it disabled for everything else:
metro.config.js
This custom resolver enables package exports only when resolving uniwind and culori, while keeping it disabled for all other packages.

Why This Works

The custom resolveRequest function:
  1. Checks the module name - If it’s uniwind or culori, it enables package exports
  2. Creates a new context - Temporarily overrides the setting for these specific packages
  3. 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
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.

Troubleshooting

If you still encounter issues after adding the custom resolver:
  1. Clear Metro cache - Run npx expo start --clear or npx react-native start --reset-cache
  2. Rebuild the app - Package export changes may require a full rebuild
  3. Check the module name - Ensure the module causing issues is included in the ['uniwind', 'culori'] array
  4. Verify Metro config - Make sure the custom resolver is defined before calling withUniwindConfig

Metro Configuration

Learn more about configuring Metro for Uniwind
Available in Uniwind 1.2.0+Install react-native-safe-area-context and wire safe area insets to Uniwind.
This applies only to the open source version of Uniwind. In the Pro version, insets are injected automatically from C++.

Setup

  1. Add the dependency:
  1. Wrap your root layout with SafeAreaListener and forward insets to Uniwind:
Add the listener once at the root of your app to keep all screens in sync.

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
Each utility also supports 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

ClassExampleEffect
p-safeclassName="p-safe"Sets all padding to the current inset values
pt-safeclassName="pt-safe"Top padding equals top inset
m-safeclassName="m-safe"Sets all margins to the inset values
inset-safeclassName="inset-safe"Sets top/bottom/left/right position to inset values
top-safeclassName="top-safe"Top position equals top inset
y-safeclassName="y-safe"Top and bottom positions equal their insets
pt-safe-or-4className="pt-safe-or-4"Top padding is Math.max(topInset, 16)
pb-safe-offset-4className="pb-safe-offset-4"Bottom padding is bottomInset + 16
top-safe-offset-4className="top-safe-offset-4"Top position is topInset + 16

Positioning Examples

Use inset utilities for absolutely positioned elements that need to respect safe areas:
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.

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-rnw and uniwind/vite for 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

Experimental - This is a community-driven package, not officially maintained by the Uniwind team.
@a16n-dev has created uniwind-plugin-next, a webpack plugin that integrates Uniwind into Next.js applications with SSR support.

GitHub Repository

View source and installation instructions

Live Demo

See the plugin in action

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
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.
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

shadcn/ui for React Native - beautifully crafted components

HeroUI Native

Beautiful, fast and modern React Native UI library

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
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 - 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
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).

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!
Want your UI kit featured here? We collaborate closely with library authors to ensure optimal integration and performance. Reach out on GitHub Discussions!

Using Other Component Libraries

Uniwind works with any React Native component library. For libraries that don’t natively support className, you can use withUniwind to add className support:

withUniwind API

Learn how to add className support to any component

Third-party Components

See examples of using Uniwind with various component libraries