Skip to main content

Overview

NativeWind fully supports CSS variables (custom properties), allowing you to create dynamic, theme-aware applications. Unlike traditional React Native styling, CSS variables can be changed at runtime, enabling features like dark mode, user themes, and dynamic theming.

Basic Usage

Defining Variables

Define CSS variables in your theme file using the @theme block:
theme.css
@theme {
  --color-primary: #3b82f6;
  --color-secondary: #8b5cf6;
  --color-background: #ffffff;
  --color-text: #000000;
  
  --spacing-unit: 8px;
  --border-radius: 8px;
}

Using Variables in Utilities

Reference variables in custom utilities:
theme.css
@utility bg-primary {
  background-color: var(--color-primary);
}

@utility text-primary {
  color: var(--color-primary);
}
Use them in your components:
import { View, Text } from "react-native";

export default function Card() {
  return (
    <View className="bg-primary p-4 rounded">
      <Text className="text-white">Primary Card</Text>
    </View>
  );
}

Runtime Variables

NativeWind exposes APIs for working with CSS variables at runtime, enabling dynamic theming.

useUnstableNativeVariable

Access and modify CSS variables from your components:
import { View, Text, Button } from "react-native";
import { useUnstableNativeVariable } from "nativewind";

export default function ThemeDemo() {
  const [primaryColor, setPrimaryColor] = useUnstableNativeVariable("--color-primary");
  
  const changeTheme = () => {
    setPrimaryColor("#ef4444"); // Change to red
  };
  
  return (
    <View className="flex-1 p-4">
      <View className="bg-primary p-6 rounded-lg">
        <Text className="text-white text-lg">Current Theme</Text>
        <Text className="text-white/70">Primary: {primaryColor}</Text>
      </View>
      <Button title="Change Theme" onPress={changeTheme} />
    </View>
  );
}
The useUnstableNativeVariable hook is marked as unstable and its API may change in future versions. Use with caution in production applications.

VariableContextProvider

Provide CSS variables to a subtree of your application:
import { View, Text } from "react-native";
import { VariableContextProvider } from "nativewind";

export default function App() {
  return (
    <VariableContextProvider
      value={{
        "--color-primary": "#3b82f6",
        "--color-secondary": "#8b5cf6",
      }}
    >
      <View className="bg-primary p-4">
        <Text className="text-secondary">Themed content</Text>
      </View>
    </VariableContextProvider>
  );
}

Nested Contexts

You can nest VariableContextProvider components to create scoped themes:
import { View, Text } from "react-native";
import { VariableContextProvider } from "nativewind";

export default function NestedThemes() {
  return (
    <VariableContextProvider value={{ "--color-primary": "#3b82f6" }}>
      <View className="bg-primary p-4">
        <Text className="text-white">Blue Theme</Text>
        
        <VariableContextProvider value={{ "--color-primary": "#ef4444" }}>
          <View className="bg-primary p-4 mt-4">
            <Text className="text-white">Red Theme (nested)</Text>
          </View>
        </VariableContextProvider>
      </View>
    </VariableContextProvider>
  );
}

Dark Mode with CSS Variables

Implement dark mode using CSS variables and the dark: variant:
@theme {
  /* Light mode colors */
  --color-background: #ffffff;
  --color-text: #000000;
  --color-card: #f3f4f6;
  --color-border: #e5e7eb;
  
  /* Dark mode colors */
  @media (prefers-color-scheme: dark) {
    --color-background: #000000;
    --color-text: #ffffff;
    --color-card: #1f2937;
    --color-border: #374151;
  }
}
NativeWind automatically detects your device’s color scheme using React Native’s useColorScheme() hook and applies the appropriate CSS variables.

Semantic Color System

Create a semantic color system using CSS variables:
theme.css
@theme {
  /* Base palette */
  --color-blue-500: #3b82f6;
  --color-red-500: #ef4444;
  --color-green-500: #10b981;
  --color-gray-100: #f3f4f6;
  --color-gray-900: #111827;
  
  /* Semantic colors - light mode */
  --color-primary: var(--color-blue-500);
  --color-error: var(--color-red-500);
  --color-success: var(--color-green-500);
  --color-background: white;
  --color-surface: var(--color-gray-100);
  --color-text: var(--color-gray-900);
  
  /* Semantic colors - dark mode */
  @media (prefers-color-scheme: dark) {
    --color-background: black;
    --color-surface: var(--color-gray-900);
    --color-text: var(--color-gray-100);
  }
}
Usage:
<View className="bg-[var(--color-surface)]">
  <Text className="text-[var(--color-text)]">Semantic theming</Text>
  <Text className="text-[var(--color-primary)]">Primary action</Text>
  <Text className="text-[var(--color-error)]">Error message</Text>
</View>

Platform-Specific Variables

Define different variable values for each platform:
theme.css
:root {
  /* Default values */
  --shadow-color: rgba(0, 0, 0, 0.1);
  --shadow-elevation: 3;
  
  @media ios {
    --shadow-color: rgba(0, 0, 0, 0.15);
    --shadow-radius: 4px;
    --shadow-offset-y: 2px;
  }
  
  @media android {
    --shadow-elevation: 4;
  }
}

Variable Namespaces

Organize variables using prefixes:
theme.css
@theme {
  /* Color variables */
  --color-primary: #3b82f6;
  --color-secondary: #8b5cf6;
  
  /* Spacing variables */
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --spacing-xl: 32px;
  
  /* Typography variables */
  --font-size-xs: 12px;
  --font-size-sm: 14px;
  --font-size-base: 16px;
  --font-size-lg: 18px;
  --font-size-xl: 24px;
  
  /* Animation variables */
  --duration-fast: 150ms;
  --duration-base: 300ms;
  --duration-slow: 500ms;
  
  /* Elevation variables */
  --elevation-sm: 2;
  --elevation-md: 4;
  --elevation-lg: 8;
  --elevation-xl: 16;
}

Computed Variables

Create variables that reference other variables:
theme.css
@theme {
  /* Base values */
  --spacing-unit: 8px;
  --color-primary: #3b82f6;
  
  /* Computed values */
  --spacing-2x: calc(var(--spacing-unit) * 2);
  --spacing-3x: calc(var(--spacing-unit) * 3);
  --spacing-half: calc(var(--spacing-unit) / 2);
  
  /* Color variations */
  --color-primary-light: color-mix(in srgb, var(--color-primary) 70%, white);
  --color-primary-dark: color-mix(in srgb, var(--color-primary) 70%, black);
}
color-mix() may have limited support in React Native. Test thoroughly on your target platforms.

Dynamic Theme Switching

Implement a complete theme switching system:
import React, { createContext, useContext, useState } from "react";
import { VariableContextProvider } from "nativewind";

const themes = {
  light: {
    "--color-background": "#ffffff",
    "--color-text": "#000000",
    "--color-primary": "#3b82f6",
  },
  dark: {
    "--color-background": "#000000",
    "--color-text": "#ffffff",
    "--color-primary": "#60a5fa",
  },
  ocean: {
    "--color-background": "#0c4a6e",
    "--color-text": "#e0f2fe",
    "--color-primary": "#06b6d4",
  },
};

type ThemeName = keyof typeof themes;

const ThemeContext = createContext<{
  theme: ThemeName;
  setTheme: (theme: ThemeName) => void;
}>({
  theme: "light",
  setTheme: () => {},
});

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = useState<ThemeName>("light");
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <VariableContextProvider value={themes[theme]}>
        {children}
      </VariableContextProvider>
    </ThemeContext.Provider>
  );
}

export const useTheme = () => useContext(ThemeContext);

Performance Considerations

CSS variables are resolved at runtime, but NativeWind optimizes this process during the build step. Variables defined in @theme are processed at build time when possible.
Changing CSS variables through useUnstableNativeVariable or VariableContextProvider only re-renders components that consume those specific variables, not the entire tree.
Static variable values are resolved at build time for maximum performance. Only dynamic runtime changes incur a performance cost.

Best Practices

Semantic Naming

Use semantic names like --color-primary instead of --color-blue to make theming easier

Namespace Variables

Group related variables with prefixes like --color-*, --spacing-*, --font-*

Document Your System

Maintain a reference of all available variables and their purposes

Test Across Platforms

Always test variable-based styling on iOS, Android, and web platforms

Debugging

Inspect current variable values at runtime:
import { useUnstableNativeVariable } from "nativewind";

function DebugVariables() {
  const [primary] = useUnstableNativeVariable("--color-primary");
  const [background] = useUnstableNativeVariable("--color-background");
  
  console.log("Variables:", { primary, background });
  
  return (
    <View>
      <Text>Primary: {primary}</Text>
      <Text>Background: {background}</Text>
    </View>
  );
}

Advanced: Variables in Custom Utilities

Combine CSS variables with custom utilities:
theme.css
@theme {
  --elevation-card: 4;
  --radius-card: 12px;
  --color-card-bg: #ffffff;
}

@utility card {
  background-color: var(--color-card-bg);
  border-radius: var(--radius-card);
  -rn-elevation: var(--elevation-card);
  padding: 16px;
}
Usage:
<View className="card">
  <Text>This card uses variable-based styling</Text>
</View>