Skip to main content

Overview

The withUniwind higher-order component (HOC) wraps any React Native component to add className prop support. This is essential for using third-party components with Uniwind’s Tailwind-based styling system.

Why Use withUniwind?

Many popular React Native libraries export components that don’t natively support the className prop. Instead, they accept the traditional style prop. The withUniwind wrapper bridges this gap, allowing you to use Tailwind classes with any component.
Some third-party components work out of the box! Libraries like React Native Reanimated that are built on top of React Native’s core components (View, Text, etc.) automatically support className without wrapping. You only need withUniwind when the underlying implementation uses custom native components or doesn’t forward the style prop properly.

Problem

import { SafeAreaView } from 'react-native-safe-area-context';

// ❌ This won't work - SafeAreaView is a third-party component
<SafeAreaView className="flex-1 bg-background">
  {/* content */}
</SafeAreaView>

Solution

import { withUniwind } from 'uniwind'
import { SafeAreaView } from 'react-native-safe-area-context';

const StyledSafeAreaView = withUniwind(SafeAreaView);

// ✅ This works - we've wrapped the component with withUniwind
<StyledSafeAreaView className="flex-1 bg-background">
  {/* content */}
</StyledSafeAreaView>

Automatic Prop Mapping

withUniwind automatically maps props based on their names. Any prop containing style or color in its name is automatically mapped.
No manual mapping needed! The style prop is automatically mapped to className, and color-related props get their own *ClassName variants.

Automatic Mappings

Here are some examples of how props are automatically mapped:
Original PropMapped ClassName PropExample Usage
styleclassName<Component className="p-4" />
colorcolorClassName<Component colorClassName="accent-red-500" />
backgroundColorbackgroundColorClassName<Component backgroundColorClassName="accent-blue-500" />
borderColorborderColorClassName<Component borderColorClassName="accent-gray-300" />
tintColortintColorClassName<Component tintColorClassName="accent-green-500" />

Example: Using Auto-Mapped Color Props

import { withUniwind } from 'uniwind'
import { ActivityIndicator } from 'react-native-activity-indicator'

const StyledActivityIndicator = withUniwind(ActivityIndicator)

// Use colorClassName instead of the color prop
<StyledActivityIndicator
  colorClassName="accent-blue-500 dark:accent-blue-300"
  size="large"
/>

Custom Prop Mapping

For advanced use cases, you can define custom mappings to map className props to component props. This is useful for components with non-standard prop names like width, size, fill, or stroke.

How Mapping Works

The mapping object structure is:
withUniwind(Component, {
  targetProp: {                    // The component's original prop name
    fromClassName: 'myClassName',  // The className prop you'll use in JSX
    styleProperty: 'width',        // (Optional) Which CSS property to extract
  },
})
Understanding the structure:
  • Object key (targetProp) → The prop that the component actually receives
  • fromClassName → The new className prop you’ll write in your JSX
  • styleProperty → Which CSS property value to extract and pass to the target prop

Example: Mapping a Width Prop

Some components accept a width prop as a number or string, not a style object. Here’s how to map it:
import { withUniwind } from 'uniwind'
import { ProgressBar } from 'some-library'

const StyledProgressBar = withUniwind(ProgressBar, {
  width: {
    fromClassName: 'widthClassName',
    styleProperty: 'width',
  },
})

// Usage: pass Tailwind width classes via widthClassName
<StyledProgressBar widthClassName="w-64" progress={0.5} />
// The component receives: width={256}

Example: Mapping a Size Prop

import { withUniwind } from 'uniwind'
import { Avatar } from 'some-library'

const StyledAvatar = withUniwind(Avatar, {
  size: {
    fromClassName: 'sizeClassName',
    styleProperty: 'width',  // Extract width value for the size prop
  },
})

// Usage: use width classes to control size
<StyledAvatar sizeClassName="w-12" source={{ uri: '...' }} />
// The component receives: size={48}

Example: SVG Stroke and Fill

SVG components often use stroke and fill props that accept color values:
import { withUniwind } from 'uniwind'
import { Path } from 'react-native-svg'

export const StyledPath = withUniwind(Path, {
  stroke: {
    fromClassName: 'strokeClassName',
    styleProperty: 'accentColor',
  },
  fill: {
    fromClassName: 'fillClassName',
    styleProperty: 'accentColor',
  },
})

// Usage: use accent-* classes to set colors
<StyledPath
  strokeClassName="accent-red-500 dark:accent-blue-100"
  fillClassName="accent-transparent"
  d="M10 10 H 90 V 90 H 10 Z"
/>
Use accent-* classes when you need to extract a color value. The accentColor style property is specifically designed for this purpose in Uniwind.

Mapping Entire Style Objects

If you omit styleProperty, the entire resolved style object is passed to the target prop:
import { withUniwind } from 'uniwind'
import { CustomComponent } from 'some-library'

export const StyledCustomComponent = withUniwind(CustomComponent, {
  containerStyle: {
    fromClassName: 'containerClassName',
    // No styleProperty - maps the entire style object
  },
})

// Usage: all styles are passed as an object to containerStyle
<StyledCustomComponent containerClassName="p-4 bg-red-500 rounded-lg" />
// The component receives: containerStyle={{ padding: 16, backgroundColor: '...', borderRadius: 8 }}

Third-Party UI Components

import { withUniwind } from 'uniwind'
import { Button } from 'react-native-paper'

const StyledButton = withUniwind(Button)

export const MyButton = () => (
  <StyledButton
    className="m-4"
    backgroundColorClassName="accent-blue-500"
    mode="contained"
  >
    Press me
  </StyledButton>
)

API Reference

Function Signature

withUniwind<T>(Component: T, mappings?: PropMappings): T

Parameters

Component
React.ComponentType
required
The React component to wrap with className support.
mappings
PropMappings
Optional custom prop mappings. Each mapping defines how to convert a className prop to a component prop.Mapping structure:
{
  [targetProp: string]: {
    fromClassName: string,      // The className prop name to create
    styleProperty?: string       // Optional CSS property to extract (omit to use entire style)
  }
}

Return Value

StyledComponent
React.ComponentType
A wrapped component that accepts className and auto-generated *ClassName props, in addition to all original component props.

Best Practices

Create reusable styled components: Define your wrapped components in a separate file and export them for reuse throughout your app.
// components/styled.ts
import { withUniwind } from 'uniwind'
import { SafeAreaView } from 'react-native-safe-area-context'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'

export const StyledSafeAreaView = withUniwind(SafeAreaView)
export const StyledKeyboardAwareScrollView = withUniwind(KeyboardAwareScrollView)
Performance consideration: Wrap components at the module level (outside your component functions) to avoid recreating the wrapper on every render.