Skip to main content

Overview

Tailwind v4 lets you extend and override utilities directly in global.css using the @utility and @theme directives โ€” no config file required. This guide covers three common scenarios:
  1. Variable-driven utilities โ€” create a utility whose value is injected at runtime via a CSS variable
  2. Brand-new utilities โ€” add a class that has no Tailwind equivalent
  3. Overriding existing utilities โ€” change what a built-in Tailwind class does

Variable-Driven Utilities

Sometimes you need a utility class whose value isnโ€™t known until runtime. A common example is padding that accounts for a navigation header height, which is only available after the layout renders.

Example: p-safe-header

Step 1 โ€” Declare the variable in global.css Use @theme static so the variable is always available in JavaScript even before it is updated, and define the utility with @utility:
global.css
@import 'tailwindcss';
@import 'uniwind';

@theme static {
  --header-height: 0px;
}

@utility p-safe-header {
  padding-top: var(--header-height);
}
@theme static ensures --header-height is registered with Uniwind even if it is never used directly in a className. This is required for updateCSSVariables to pick it up.
Step 2 โ€” Inject the real value at runtime Read the header height from react-navigation and push it into the CSS variable using Uniwind.updateCSSVariables:
import { useEffect } from 'react'
import { useNavigation } from '@react-navigation/native'
import { Uniwind } from 'uniwind'

export const useInjectHeaderHeight = () => {
  const navigation = useNavigation()

  useEffect(() => {
    const unsubscribe = navigation.addListener('layout', (e) => {
      const { height } = e.data.layout

      Uniwind.updateCSSVariables(Uniwind.currentTheme, {
        '--header-height': height,
      })
    })

    return unsubscribe
  }, [navigation])
}
Step 3 โ€” Use the utility
import { View } from 'react-native'

export const Screen = () => (
  <View className="p-safe-header flex-1 bg-background">
    {/* content sits below the header */}
  </View>
)

Brand-New Custom Utilities

Use @utility to define a class that doesnโ€™t exist in Tailwind at all. The CSS you write inside it maps directly to React Native style properties.

Example: card-shadow

global.css
@import 'tailwindcss';
@import 'uniwind';

@utility card-shadow {
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
Usage:
import { View, Text } from 'react-native'

export const Card = () => (
  <View className="card-shadow rounded-lg p-4 bg-white">
    <Text className="text-foreground text-base">Card content</Text>
  </View>
)
Custom utility names must be kebab-case. Tailwind scans your source files for them at build time, so the same dynamic class name rules apply โ€” always write the full class name in source.

Overriding Existing Tailwind Utilities

Override via @theme โ€” change values only

The most common approach: keep the utility name but change what value it resolves to. This works for any design token in Tailwindโ€™s scale.
global.css
@import 'tailwindcss';
@import 'uniwind';

@theme {
  /* p-4 now applies 20px instead of the default 16px */
  --spacing-4: 20px;

  /* rounded-lg now uses 16px instead of 8px */
  --radius-lg: 16px;
}
All utilities built on those tokens (p-4, px-4, py-4, rounded-lg, etc.) update automatically โ€” no need to touch individual class definitions.

Override via @utility โ€” replace the generated CSS

Use @utility when you want to completely change what a class does, not just adjust its value. Example: make border always use --color-primary By default, border only sets border-width and border-style. Override it to also lock in the border color:
global.css
@import 'tailwindcss';
@import 'uniwind';

@utility border {
  border-width: 1px;
  border-style: solid;
  border-color: var(--color-primary);
}
Now border always renders with the primary color instead of default value (black).
import { View } from 'react-native'

export const PrimaryBorderCard = () => (
  <View className="border rounded-lg p-4">
    {/* border is --color-primary */}
  </View>
)

Best Practices

Prefer @theme for value overrides. It keeps the semantic class name and updates all related utilities in one place.
Use @utility for structural changes or new classes. Reserve it for cases where the default CSS itself needs to change, not just its values.
Only use CSS properties that React Native supports. Web-only properties like background-image, display: grid, or position: fixed have no effect on native.

Global CSS

Full reference for @theme, @utility, and global.css structure

updateCSSVariables

Inject CSS variable values at runtime

useCSSVariable

Read CSS variable values in JavaScript