> ## Documentation Index
> Fetch the complete documentation index at: https://docs.uniwind.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting

> Common issues and solutions for Uniwind — the React Native Tailwind CSS library. Setup errors, theming questions, className merging, migration from NativeWind, and performance tips.

## Common Questions

<AccordionGroup>
  <Accordion title="How do I include custom fonts?" icon="font">
    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.

    <Info>
      **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.
    </Info>

    ## Expo Projects

    ### Step 1: Install and configure expo-font

    Add the font files to your project and configure them in `app.json`:

    ```json app.json theme={null}
    {
      "expo": {
        "plugins": [
          [
            "expo-font",
            {
              "fonts": [
                "./assets/fonts/Roboto-Regular.ttf",
                "./assets/fonts/Roboto-Medium.ttf",
                "./assets/fonts/Roboto-Bold.ttf",
                "./assets/fonts/FiraCode-Regular.ttf"
              ]
            }
          ]
        ]
      }
    }
    ```

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

    ### Step 2: Define font families in global.css

    Configure your font families and text sizes using CSS variables in the `@theme` directive:

    ```css global.css theme={null}
    @import 'tailwindcss';
    @import 'uniwind';

    @theme {
      /* Other values */
      /* ... */

      /* Font families */
      --font-sans: 'Roboto-Regular';
      --font-sans-medium: 'Roboto-Medium';
      --font-sans-bold: 'Roboto-Bold';
      --font-mono: 'FiraCode-Regular';
    }
    ```

    <Warning>
      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'`.
    </Warning>

    ### Step 3: Use font classes in your components

    Now you can use the configured font families with Tailwind classes:

    ```tsx theme={null}
    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>
      </>
    )
    ```

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

    ```js react-native.config.js theme={null}
    module.exports = {
      project: {
        ios: {},
        android: {},
      },
      assets: ['./assets/fonts'],
    };
    ```

    ### Step 2: Link the fonts

    Run the following command to link your fonts:

    ```bash theme={null}
    npx react-native-asset
    ```

    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:

    ```css global.css theme={null}
    @import 'tailwindcss';
    @import 'uniwind';

    @theme {
      --font-sans: 'Roboto-Regular';
      --font-sans-medium: 'Roboto-Medium';
      --font-sans-bold: 'Roboto-Bold';
      --font-mono: 'FiraCode-Regular';
    }
    ```

    ## Platform-Specific Fonts

    You can define different fonts for different platforms using `@variant`:

    ```css global.css theme={null}
    @layer theme {
      :root {
        /* Default fonts */
        --font-sans: 'Roboto-Regular';

        /* iOS-specific fonts */
        @variant ios {
          --font-sans: 'SF Pro Text';
        }

        /* Android-specific fonts */
        @variant android {
          --font-sans: 'Roboto-Regular';
        }

        /* Web-specific fonts */
        @variant web {
          --font-sans: 'system-ui', sans-serif;
        }
      }
    }
    ```

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

    <Card title="Platform Selectors" icon="react" href="/api/platform-select">
      Learn more about using platform-specific styles
    </Card>
  </Accordion>

  <Accordion title="How can I style components based on prop values (data selectors)?" icon="filter">
    Use data selectors with Tailwind’s `data-[...]` variant syntax to apply styles conditionally based on your own data-\* props.

    ## Basic usage

    ```tsx theme={null}
    import { View } from 'react-native'

    <View
      data-state={isOpen ? 'open' : 'closed'}
      className="px-4 py-2 rounded border data-[state=open]:bg-muted/50 data-[state=closed]:bg-transparent"
    />
    ```

    * 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

    <Card title="Data Selectors" icon="code" href="/api/data-selectors">
      Full documentation and examples
    </Card>
  </Accordion>

  <Accordion title="Where should I put global.css in Expo Router?" icon="folder-tree">
    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.

    ## Recommended Setup

    ### Step 1: Create global.css in the project root

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

    ```
    your-project/
    ├── app/
    │   ├── _layout.tsx
    │   └── index.tsx
    ├── components/
    ├── global.css         // ✅ Put it here
    └── package.json
    ```

    ```css global.css theme={null}
    @import 'tailwindcss';
    @import 'uniwind';

    /* Your custom CSS and themes */
    ```

    ### Step 2: Import in your root layout

    Import the CSS file in your root layout file (`app/_layout.tsx`):

    ```tsx app/_layout.tsx theme={null}
    import '../global.css' // Import at the top

    export default function RootLayout() {
      // Your layout code
    }
    ```

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

    ### Step 3: Configure Metro

    Point Metro to your CSS file:

    ```js metro.config.js theme={null}
    module.exports = withUniwindConfig(config, {
      cssEntryFile: './global.css',
    });
    ```

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

    <Tip>
      With `global.css` in the root, Tailwind will automatically scan all directories (app, components, etc.) without needing `@source` directives.
    </Tip>

    ## Alternative: App Directory

    You can also place `global.css` inside the `app` directory:

    ```
    your-project/
    ├── app/
    │   ├── _layout.tsx
    │   ├── global.css     // Alternative location
    │   └── index.tsx
    ├── components/
    └── package.json
    ```

    Then import it in `_layout.tsx`:

    ```tsx app/_layout.tsx theme={null}
    import './global.css' // Note: different path

    export default function RootLayout() {
      // Your layout code
    }
    ```

    And update Metro config:

    ```js metro.config.js theme={null}
    module.exports = withUniwindConfig(config, {
      cssEntryFile: './app/global.css',
    });
    ```

    <Warning>
      **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:

      ```css app/global.css theme={null}
      @import 'tailwindcss';
      @import 'uniwind';

      @source '../components';
      ```

      The location of `global.css` determines your app root. Tailwind will only scan for classNames starting from that directory.
    </Warning>

    <Card title="Global CSS Location Guide" icon="css" href="/theming/global-css">
      Learn more about configuring global.css
    </Card>

    <Card title="Monorepos & @source" icon="folder-tree" href="/monorepos">
      Understand how @source works with multiple directories
    </Card>
  </Accordion>

  <Accordion title="Why does my app still fully reload when I change CSS?" icon="arrows-rotate">
    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:

    ```tsx App.tsx theme={null}
    import './global.css' // ⚠️ This file has many providers below

    export default function App() {
      return (
        <ReduxProvider store={store}>
          <ApolloProvider client={client}>
            <ThemeProvider>
              <AuthProvider>
                <NavigationProvider>
                  <NotificationProvider>
                    <AnalyticsProvider>
                      {/* Your app */}
                    </AnalyticsProvider>
                  </NotificationProvider>
                </NavigationProvider>
              </AuthProvider>
            </ThemeProvider>
          </ApolloProvider>
        </ReduxProvider>
      )
    }
    ```

    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

    ```tsx App.tsx theme={null}
    // Remove the import from here
    // import './global.css' // [!code --]

    export default function App() {
      return (
        <ReduxProvider store={store}>
          <ApolloProvider client={client}>
            <NavigationRoot /> {/* Import CSS here instead */}
          </ApolloProvider>
        </ReduxProvider>
      )
    }
    ```

    ```tsx NavigationRoot.tsx theme={null}
    import './global.css' // ✅ Import here

    export const NavigationRoot = () => {
      return (
        <NavigationContainer>
          {/* Your navigation */}
        </NavigationContainer>
      )
    }
    ```

    ### Option 2: Import in your home/main screen

    ```tsx screens/HomeScreen.tsx theme={null}
    import '../global.css' // ✅ Import in a screen component

    export const HomeScreen = () => {
      return (
        <View className="flex-1 bg-background">
          {/* Your content */}
        </View>
      )
    }
    ```

    ### Option 3: Import in Expo Router's nested layout

    If using Expo Router, move the import to a nested layout:

    ```tsx app/_layout.tsx theme={null}
    // Remove from root layout
    // import './global.css' // [!code --]

    export default function RootLayout() {
      return (
        <Providers>
          <Slot />
        </Providers>
      )
    }
    ```

    ```tsx app/(tabs)/_layout.tsx theme={null}
    import '../global.css' // ✅ Import in nested layout

    export default function TabsLayout() {
      return <Tabs />
    }
    ```

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

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

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

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

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

    <Warning>
      This is a Metro/React Native limitation, not specific to Uniwind. Any file with complex provider trees will have this issue with Fast Refresh.
    </Warning>

    <Card title="Fast Refresh Documentation" icon="react" href="https://reactnative.dev/docs/fast-refresh">
      Learn more about React Native's Fast Refresh system
    </Card>
  </Accordion>

  <Accordion title="How can I use gradients?" icon="paintbrush">
    Uniwind provides built-in gradient support using Tailwind syntax with React Native's internal implementation. No additional dependencies required!

    ## Built-in Gradient Support (Recommended)

    Use gradient classes directly with the `className` prop:

    ### Directional Gradients

    ```tsx theme={null}
    import { View } from 'react-native'

    // Left to right gradient
    <View className="bg-gradient-to-r from-indigo-500 to-pink-500 rounded size-16" />

    // Top to bottom gradient
    <View className="bg-gradient-to-b from-blue-500 to-purple-500 rounded size-16" />

    // Diagonal gradient (top-left to bottom-right)
    <View className="bg-gradient-to-br from-green-400 to-blue-500 rounded size-16" />
    ```

    **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}`:

    ```tsx theme={null}
    import { View } from 'react-native'

    // 90 degree gradient
    <View className="bg-linear-90 from-indigo-500 via-sky-500 to-pink-500 rounded size-16" />

    // 45 degree gradient
    <View className="bg-linear-45 from-red-500 to-yellow-500 rounded size-16" />

    // 180 degree gradient
    <View className="bg-linear-180 from-purple-500 to-pink-500 rounded size-16" />
    ```

    ### Multi-stop Gradients

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

    ```tsx theme={null}
    import { View } from 'react-native'

    // Three color stops
    <View className="bg-gradient-to-r from-red-500 via-yellow-500 to-green-500 rounded size-16" />

    // With multiple via colors
    <View className="bg-linear-90 from-indigo-500 via-sky-500 via-purple-500 to-pink-500 rounded size-16" />
    ```

    ### Custom Gradients with Arbitrary Values

    For complete control, use arbitrary values with custom angles and color stops:

    ```tsx theme={null}
    import { View } from 'react-native'

    // Custom angle and color stops with percentages
    <View className="bg-linear-[25deg,red_5%,yellow_60%,lime_90%,teal] rounded size-16" />

    // Complex gradient
    <View className="bg-linear-[135deg,rgba(255,0,0,0.8)_0%,rgba(0,255,0,0.6)_50%,rgba(0,0,255,0.8)_100%] rounded size-16" />
    ```

    **Syntax:** `bg-linear-[angle,color1_position,color2_position,...]`

    <Tip>
      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](https://tailwindcss.com/docs/background-image#adding-a-linear-gradient) documentation.
    </Tip>

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

    ```tsx theme={null}
    import { LinearGradient } from 'expo-linear-gradient'
    import { withUniwind } from 'uniwind'

    // Can't map className to colors array
    const StyledLinearGradient = withUniwind(LinearGradient)

    <StyledLinearGradient
      colorsClassName={['accent-red-500', 'accent-transparent']} // ?? Can't map to colors array
    />
    ```

    ### ✅ Use useCSSVariable instead

    ```tsx theme={null}
    import { LinearGradient } from 'expo-linear-gradient'
    import { useCSSVariable } from 'uniwind'

    export const GradientComponent = () => {
      const startColor = useCSSVariable('--color-indigo-500')
      const midColor = useCSSVariable('--color-pink-200')
      const endColor = useCSSVariable('--color-pink-500')

      return (
        <LinearGradient colors={[startColor, midColor, endColor]}/>
      )
    }
    ```

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

    ## Examples

    ### Card with Gradient Background

    ```tsx theme={null}
    import { View, Text } from 'react-native'

    export const GradientCard = () => (
      <View className="bg-gradient-to-br from-purple-600 via-pink-500 to-red-500 p-6 rounded-2xl">
        <Text className="text-white text-2xl font-bold">
          Beautiful Gradient Card
        </Text>
        <Text className="text-white/80 mt-2">
          Using built-in gradient support
        </Text>
      </View>
    )
    ```

    ### Button with Gradient

    ```tsx theme={null}
    import { Pressable, Text } from 'react-native'

    export const GradientButton = ({ onPress, title }) => (
      <Pressable
        onPress={onPress}
        className="bg-gradient-to-r from-blue-500 to-cyan-500 px-6 py-3 rounded-full active:opacity-80"
      >
        <Text className="text-white text-center font-semibold">
          {title}
        </Text>
      </Pressable>
    )
    ```

    ### Theme-aware Gradient

    ```tsx theme={null}
    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>
    )
    ```
  </Accordion>

  <Accordion title="How do you handle merging and deduplicating classNames?" icon="code-merge">
    Uniwind does not automatically deduplicate classNames, especially on web. When you have conflicting styles or duplicate classes, you'll need to handle merging manually.

    <Info>
      **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.
    </Info>

    ## Using tailwind-merge (Recommended)

    For proper className merging and deduplication, we recommend using [`tailwind-merge`](https://github.com/dcastil/tailwind-merge) with a utility function:

    ### Step 1: Install dependencies

    <Tabs>
      <Tab title="bun">
        ```bash theme={null}
        bun add tailwind-merge clsx
        ```
      </Tab>

      <Tab title="npm">
        ```bash theme={null}
        npm install tailwind-merge clsx
        ```
      </Tab>

      <Tab title="yarn">
        ```bash theme={null}
        yarn add tailwind-merge clsx
        ```
      </Tab>
    </Tabs>

    ### Step 2: Create a cn utility

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

    ```ts lib/utils.ts theme={null}
    import { type ClassValue, clsx } from 'clsx';
    import { twMerge } from 'tailwind-merge';

    export function cn(...inputs: ClassValue[]) {
      return twMerge(clsx(inputs));
    }
    ```

    ### Step 3: Use the cn utility

    Now you can merge classNames safely in your components:

    ```tsx theme={null}
    import { View, Text, Pressable } from 'react-native'
    import { cn } from '@/lib/utils'

    export const Button = ({ className, variant = 'default', ...props }) => {
      return (
        <Pressable
          className={cn(
            // Base styles
            'px-4 py-2 rounded-lg',
            // Variant styles
            variant === 'default' && 'bg-blue-500',
            variant === 'destructive' && 'bg-red-500',
            // Custom className (will override conflicting classes)
            className
          )}
          {...props}
        />
      )
    }

    // Usage
    <Button variant="default" className="bg-green-500" />
    // Result: bg-green-500 wins, bg-blue-500 is removed
    ```

    ## Why Use tailwind-merge?

    Without `tailwind-merge`, conflicting classes can cause issues:

    ### ❌ Without tailwind-merge

    ```tsx theme={null}
    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}`} />
    ```

    ### ✅ With tailwind-merge

    ```tsx theme={null}
    import { View } from 'react-native'
    import { cn } from '@/lib/utils'

    const baseClasses = 'bg-red-500 p-4'
    const customClasses = 'bg-blue-500'

    // tailwind-merge removes bg-red-500, keeps bg-blue-500
    // Result: clean, predictable styling
    <View className={cn(baseClasses, customClasses)} />
    ```

    ## Conditional Class Merging

    The `clsx` library inside `cn` makes conditional classes easier:

    ```tsx theme={null}
    import { View } from 'react-native'
    import { cn } from '@/lib/utils'

    export const Card = ({ isActive, isDisabled, className }) => (
      <View
        className={cn(
          'p-4 rounded-lg border',
          isActive && 'border-blue-500 bg-blue-50',
          isDisabled && 'opacity-50',
          !isDisabled && 'active:scale-95',
          className
        )}
      />
    )
    ```
  </Accordion>

  <Accordion title="How does style specificity work in Uniwind?" icon="layer-group">
    Understanding style specificity and priority is important when working with Uniwind to ensure predictable styling behavior.

    ## Inline styles override className

    In Uniwind inline styles always have higher priority than className:

    ```tsx theme={null}
    import { View } from 'react-native'

    // Inline style takes priority
    <View
      className="bg-red-500"
      style={{ backgroundColor: 'blue' }} // This wins
    />
    ```

    **Result:** The background will be blue, not red.

    <Warning>
      Inline styles always have higher priority than className. If you need to override a className style, you can use inline styles or merge classNames properly with `cn` from `tailwind-merge`.
    </Warning>

    ## Platform-specific behavior

    Specificity rules work consistently across platforms:

    ```tsx theme={null}
    import { View } from 'react-native'

    <View
      className="bg-red-500 ios:bg-blue-500 android:bg-green-500"
      style={{ backgroundColor: 'purple' }}
    />
    ```

    **Result on all platforms:** Purple background (inline style always wins)

    ## Best practices

    <Tip>
      **Use className for static styles** and inline styles only for truly dynamic values that can't be represented as classes (e.g., values from API, animation interpolations).
    </Tip>

    <Tip>
      **Use `cn` from tailwind-merge** when building component libraries to ensure predictable className overrides.
    </Tip>

    <Warning>
      **Avoid mixing className and inline styles** for the same property. Choose one approach for consistency and maintainability.
    </Warning>
  </Accordion>

  <Accordion title="How to debug 'Failed to serialize javascript object' error?" icon="bug">
    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.
    ```

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

    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

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

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

    ## 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:

    ```bash theme={null}
    watchman watch-del-all 2>/dev/null; rm -rf node_modules/.cache && npx expo start --clear
    ```

    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:

    ```bash theme={null}
    watchman watch-del-all 2>/dev/null; rm -rf node_modules/.cache && npx react-native start --reset-cache
    ```

    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:

    ```js node_modules/uniwind/dist/metro/metro-transformer.cjs theme={null}
    // 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); // [!code ++]
      Logger.error("Failed to serialize javascript object");
      return "";
    }
    return serializedObject;
    ```

    ### Step 2: Run your app

    After adding the console log, run your Metro bundler:

    ```bash theme={null}
    npx expo start --clear
    # or
    npx react-native start --reset-cache
    ```

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

    <Info>
      Include the serialization output and the code pattern causing the issue. This helps the maintainers fix the serializer to support your use case.
    </Info>

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

    <Card title="Report Serialization Issues" icon="github" href="https://github.com/uni-stack/uniwind/issues">
      Found a serialization issue? Help improve Uniwind by reporting it
    </Card>
  </Accordion>

  <Accordion title="How can I fix Metro unstable_enablePackageExports conflicts?" icon="triangle-exclamation">
    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:

    ```js metro.config.js theme={null}
    config.resolver.unstable_enablePackageExports = false
    ```

    Uniwind and its dependency (`culori`) won't work correctly because they require package exports to be enabled.

    <Warning>
      Completely disabling `unstable_enablePackageExports` will break Uniwind's module resolution.
    </Warning>

    ## The Solution

    You can selectively enable package exports only for Uniwind and its dependencies while keeping it disabled for everything else:

    ```js metro.config.js theme={null}
    const { getDefaultConfig } = require('expo/metro-config');
    const { withUniwindConfig } = require('uniwind/metro');

    const config = getDefaultConfig(__dirname);

    // Disable package exports globally (for crypto libraries, etc.)
    config.resolver.unstable_enablePackageExports = false;

    // Selectively enable package exports for Uniwind and culori
    config.resolver.resolveRequest = (context, moduleName, platform) => {
      // uniwind and its dependency (culori) require unstable_enablePackageExports to be true
      if (['uniwind', 'culori'].some((prefix) => moduleName.startsWith(prefix))) {
        const newContext = {
          ...context,
          unstable_enablePackageExports: true,
        };

        return context.resolveRequest(newContext, moduleName, platform);
      }

      // default behavior for everything else
      return context.resolveRequest(context, moduleName, platform);
    };

    module.exports = withUniwindConfig(config, {
      cssEntryFile: './src/global.css',
    });
    ```

    <Tip>
      This custom resolver enables package exports only when resolving `uniwind` and `culori`, while keeping it disabled for all other packages.
    </Tip>

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

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

    ## 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`

    <Card title="Metro Configuration" icon="train" href="/api/metro-config">
      Learn more about configuring Metro for Uniwind
    </Card>
  </Accordion>

  <Accordion title="Does Uniwind work with Vite?" icon="bolt">
    <Badge>Available in Uniwind 1.2.0+</Badge>

    Yes. Use Vite with React Native Web, Tailwind, and the Uniwind Vite plugin.

    ## Setup

    Create `vite.config.ts` in your project root:

    ```ts lines vite.config.ts theme={null}
    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'],
            }),
        ],
    })
    ```

    <Info>
      Point `cssEntryFile` to the CSS file where you import `tailwindcss` and `uniwind`. Keep it at your app root for accurate class scanning.
    </Info>

    <Tip>
      Restart Vite after changing your CSS entry or adding new themes.
    </Tip>

    <Tip>
      Storybook works via Vite too! Use the same `uniwind/vite` plugin in your Storybook Vite config and follow the React Native Web + Vite guide: [https://storybook.js.org/docs/get-started/frameworks/react-native-web-vite](https://storybook.js.org/docs/get-started/frameworks/react-native-web-vite). This gives you the web Storybook UI for visual and interaction testing.
    </Tip>
  </Accordion>

  <Accordion title="How do I enable safe area classNames?" icon="shield">
    <Badge>Available in Uniwind 1.2.0+</Badge>

    Install `react-native-safe-area-context` and wire safe area insets to Uniwind.

    <Warning>
      This applies only to the open source version of Uniwind. In the Pro version, insets are injected automatically from C++.
    </Warning>

    ## Setup

    1. Add the dependency:

    <Tabs>
      <Tab title="bun">
        ```bash theme={null}
        bun add react-native-safe-area-context
        ```
      </Tab>

      <Tab title="npm">
        ```bash theme={null}
        npm install react-native-safe-area-context
        ```
      </Tab>

      <Tab title="yarn">
        ```bash theme={null}
        yarn add react-native-safe-area-context
        ```
      </Tab>
    </Tabs>

    2. Wrap your root layout with `SafeAreaListener` and forward insets to Uniwind:

    ```tsx lines theme={null}
    import { SafeAreaListener } from 'react-native-safe-area-context'
    import { Uniwind } from 'uniwind'

    export const Root = () => (
      <SafeAreaListener
        onChange={({ insets }) => {
          Uniwind.updateInsets(insets)
        }}
      >
        {/* app content */}
      </SafeAreaListener>
    )
    ```

    <Tip>
      Add the listener once at the root of your app to keep all screens in sync.
    </Tip>

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

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

    ```tsx theme={null}
    // 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>
    ```
  </Accordion>

  <Accordion title="Does Uniwind work with Next.js?" icon="question">
    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

    <Warning>
      **Experimental** - This is a community-driven package, not officially maintained by the Uniwind team.
    </Warning>

    [@a16n-dev](https://github.com/a16n-dev) has created `uniwind-plugin-next`, a webpack plugin that integrates Uniwind into Next.js applications with SSR support.

    <CardGroup cols={2}>
      <Card title="GitHub Repository" icon="github" href="https://github.com/a16n-dev/uniwind-plugin-next">
        View source and installation instructions
      </Card>

      <Card title="Live Demo" icon="globe" href="https://uniwind-next.a16n.dev/">
        See the plugin in action
      </Card>
    </CardGroup>

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

    <Tip>
      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.
    </Tip>
  </Accordion>

  <Accordion title="What UI kits work well with Uniwind?" icon="layer-group">
    Uniwind works with any React Native component library, but we've worked closely with UI kit teams to ensure the best integration and performance.

    ## Recommended UI Kits

    <CardGroup cols={2}>
      <Card title="React Native Reusables" icon="shapes" href="https://reactnativereusables.com/">
        shadcn/ui for React Native - beautifully crafted components
      </Card>

      <Card title="HeroUI Native" icon="star" href="https://github.com/heroui-inc/heroui-native">
        Beautiful, fast and modern React Native UI library
      </Card>
    </CardGroup>

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

    <Tip>
      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!
    </Tip>

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

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

    ## 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!

    <Tip>
      Want your UI kit featured here? We collaborate closely with library authors to ensure optimal integration and performance. Reach out on GitHub Discussions!
    </Tip>

    ## Using Other Component Libraries

    Uniwind works with any React Native component library. For libraries that don't natively support `className`, you can use [`withUniwind`](/api/with-uniwind) to add className support:

    ```tsx theme={null}
    import { withUniwind } from 'uniwind'
    import { SomeComponent } from 'some-library'

    const StyledComponent = withUniwind(SomeComponent)

    <StyledComponent className="bg-blue-500 p-4" />
    ```

    <Card title="withUniwind API" icon="code" href="/api/with-uniwind">
      Learn how to add className support to any component
    </Card>

    <Card title="Third-party Components" icon="boxes" href="/components/other-components">
      See examples of using Uniwind with various component libraries
    </Card>
  </Accordion>
</AccordionGroup>
