Loading...
Loading...
Uniwind (Tailwind CSS v4 for React Native) best practices, setup, theming, styling, and HeroUI Native integration. Use when writing, reviewing, or fixing Uniwind code. Triggers on: uniwind, className on RN components, global.css with @import 'uniwind', withUniwindConfig, metro.config.js setup, dark:/light: theming, platform selectors (ios:/android:/native:/web:), data selectors, responsive breakpoints, CSS variables, useUniwind, withUniwind, useResolveClassNames, useCSSVariable, tailwind-variants, HeroUI Native with Uniwind, Uniwind Pro (animations, shadow tree, transitions), NativeWind migration. Also triggers on setup troubleshooting: "check my config", "styles not working", "className not applying", "audit Uniwind setup".
npx skill4agent add imfa-solutions/skills uniwind-best-practicesUniwind 1.3+ / Tailwind CSS v4 / React Native 0.76+ / Expo SDK 52+
| User's Topic | Read This Reference File FIRST |
|---|---|
| Installation, setup, metro config, global.css, TypeScript, Vite, Expo Router, monorepos | |
Theming, dark mode, CSS variables, | |
Styling components, className bindings, third-party components, | |
Supported/unsupported classes, safe area classes ( | |
| Uniwind Pro, animations, transitions, shadow tree, native insets, Reanimated, C++ engine, Pro installation | |
| NativeWind migration, troubleshooting, setup diagnostics, common errors, FAQ, debug mode | |
docs.uniwind.devbun addnpm installnpxpod install.npmrc.yarnrc.yml.envpackage.jsonmetro.config.jsbabel.config.jsglobal.csstsconfig.jsonapp.jsontrustedDependenciesuniwinduniwind-prouniwind.npmrc.yarnrc.yml.env- "uniwind" or "uniwind-pro" must be in dependencies
- "tailwindcss" must be v4+ (^4.0.0)
- For Pro: "react-native-nitro-modules", "react-native-reanimated", "react-native-worklets" required- Must import { withUniwindConfig } from 'uniwind/metro'
- withUniwindConfig MUST be the OUTERMOST wrapper
- cssEntryFile must be a RELATIVE path string (e.g., './src/global.css')
- Never use path.resolve() or absolute paths for cssEntryFile- Must contain: @import 'tailwindcss'; AND @import 'uniwind';
- Must be imported in App.tsx or root layout, NOT in index.ts/index.js
- Location determines app root — Tailwind scans from this directory- Must include 'react-native-worklets/plugin' in plugins array- uniwind-types.d.ts should be included in tsconfig.json or placed in src/app dir
- Run Metro server to auto-generate types| Symptom | Cause | Fix |
|---|---|---|
| Styles not applying | Missing | Add both imports |
| Styles not applying | global.css imported in index.js | Move import to App.tsx or _layout.tsx |
| Classes not detected | global.css in nested dir, components elsewhere | Add |
| TypeScript errors | Missing types file | Run Metro to generate uniwind-types.d.ts |
| Hot reload broken | global.css in index.ts | Move to App.tsx |
| Metro crash | Absolute path in cssEntryFile | Use relative: |
| withUniwindConfig not outermost | Other wrapper wraps Uniwind | Swap order so Uniwind is outermost |
| Pro animations broken | Missing Babel plugin | Add |
| Pro not working | Missing native rebuild | Run |
| Pro postinstall failed | Package manager blocks scripts | Advise user to whitelist in |
@import 'tailwindcss'@tailwind basecssInteroptailwind.config.jsglobal.css@theme@layer themeUniwind.setTheme()@theme<Text>--font-sans: 'Roboto-Regular''Roboto', sans-serifpolyfills: { rem: 14 }import { View, Text, Pressable } from 'react-native'
<View className="flex-1 bg-background p-4">
<Text className="text-foreground text-lg font-bold">Title</Text>
<Pressable className="bg-primary px-6 py-3 rounded-lg active:opacity-80">
<Text className="text-white text-center font-semibold">Button</Text>
</Pressable>
</View>// Map objects with complete class names
const variants = {
primary: 'bg-blue-500 text-white',
danger: 'bg-red-500 text-white',
ghost: 'bg-transparent text-foreground',
}
<Pressable className={variants[variant]} />
// Ternary with complete strings
<View className={isActive ? 'bg-primary' : 'bg-muted'} />
// tailwind-variants for complex components
import { tv } from 'tailwind-variants'
const button = tv({
base: 'font-semibold rounded-lg px-4 py-2',
variants: {
color: { primary: 'bg-blue-500 text-white', secondary: 'bg-gray-500 text-white' },
size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg' },
},
defaultVariants: { color: 'primary', size: 'md' },
})
<Pressable className={button({ color: 'primary', size: 'lg' })} />// withUniwind — wrap once, use everywhere (recommended)
import { withUniwind } from 'uniwind'
import { SafeAreaView } from 'react-native-safe-area-context'
const StyledSafeArea = withUniwind(SafeAreaView)
<StyledSafeArea className="flex-1 bg-background" />
// useResolveClassNames — one-off usage
import { useResolveClassNames } from 'uniwind'
const styles = useResolveClassNames('bg-blue-500 p-4 rounded-lg')
<ThirdPartyComponent style={styles} />// Just use dark: prefix — works out of the box
<View className="bg-white dark:bg-gray-900">
<Text className="text-black dark:text-white">Themed</Text>
</View>global.cssdark:/* global.css */
@import 'tailwindcss';
@import 'uniwind';
@layer theme {
:root {
@variant light {
--color-background: #ffffff;
--color-foreground: #000000;
--color-primary: #3b82f6;
--color-card: #ffffff;
--color-border: #e5e7eb;
}
@variant dark {
--color-background: #000000;
--color-foreground: #ffffff;
--color-primary: #3b82f6;
--color-card: #1f2937;
--color-border: #374151;
}
}
}// Auto-adapts to current theme — no dark: prefix needed
<View className="bg-background border border-border p-4 rounded-lg">
<Text className="text-foreground">Themed card</Text>
</View>import { Uniwind, useUniwind } from 'uniwind'
Uniwind.setTheme('dark') // Switch to dark
Uniwind.setTheme('light') // Switch to light
Uniwind.setTheme('system') // Follow device
const { theme, hasAdaptiveThemes } = useUniwind() // Reactive hookextraThemes@variant<View className="ios:pt-12 android:pt-6 web:pt-4" />
<View className="native:bg-blue-500 web:bg-gray-500" /> // native = iOS + Android@theme@layer theme {
:root {
@media ios { --font-sans: "SF Pro Text"; }
@media android { --font-sans: "Roboto-Regular"; }
}
}data-[prop=value]:utility<Pressable
data-selected={isSelected}
className="border rounded px-3 py-2 data-[selected=true]:ring-2 data-[selected=true]:ring-primary"
/>sm:md:lg:xl:2xl:<View className="p-4 sm:p-6 lg:p-8">
<Text className="text-base sm:text-lg lg:text-xl">Responsive</Text>
</View>@theme { --breakpoint-tablet: 820px; }@utility@utility h-hairline { height: hairlineWidth(); }
@utility text-scaled { font-size: fontScale(); }
@utility bg-adaptive { background-color: light-dark(#ffffff, #1f2937); }uniwind-pro// Keyframe animations — just add className
<View className="animate-spin" />
<View className="animate-bounce" />
<View className="animate-pulse" />
// Transitions — smooth property changes
<Pressable className="bg-blue-500 active:bg-blue-600 transition-colors duration-300" />
<View className={`transition-opacity duration-500 ${visible ? 'opacity-100' : 'opacity-0'}`} />
<Pressable className="active:scale-95 transition-transform duration-150" />SafeAreaListenerclassNametailwind-variantswithUniwind()bg-primarytext-foreground@import 'tailwindcss'@import 'uniwind'withUniwinduseResolveClassNamesios:/android:/native:Platform.select()@theme| Priority | File | Covers |
|---|---|---|
| Read first for setup | references/quickstart-setup.md | Installation, Metro config, global.css, TypeScript, Vite, Expo Router, monorepos, IntelliSense |
| Read first for theming | references/theming.md | Light/dark, custom themes, CSS variables, |
| Read first for styling | references/components-styling.md | RN component bindings, |
| Read first for class support | references/supported-classnames.md | Supported/unsupported classes, safe area ( |
| Read first for Pro | references/pro-features.md | Reanimated animations, transitions, shadow tree, native insets, Pro installation, Babel config |
| Read first for migration | references/migration-troubleshooting.md | NativeWind migration (10-step), setup diagnostics, common errors, FAQ, debug mode |