Skip to main content

Overview

NativeWind provides full TypeScript support with automatic type generation for the className prop. This guide covers setup, configuration, and best practices for using TypeScript with NativeWind.
NativeWind automatically generates TypeScript declarations when you run your Metro bundler.

Automatic Type Generation

NativeWind generates a nativewind-env.d.ts file that adds the className prop to all React Native components.
1

Configure Metro

The withNativewind Metro wrapper automatically generates types:
metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const { withNativewind } = require("nativewind/metro");

const config = getDefaultConfig(__dirname);

module.exports = withNativewind(config, {
  typescriptEnvPath: "nativewind-env.d.ts", // Default path
});
2

Run your dev server

Start Metro to generate the type declarations:
npm start
# or
expo start
This creates nativewind-env.d.ts in your project root:
nativewind-env.d.ts
/// <reference types="react-native-css/types" />

// NOTE: This file should not be edited and should be committed 
// with your source code. It is generated by react-native-css.
3

Include in tsconfig.json

Ensure the generated file is included in your TypeScript configuration:
tsconfig.json
{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true,
    "noEmit": true,
    "allowJs": true
  },
  "include": [
    "**/*.ts",
    "**/*.tsx",
    "nativewind-env.d.ts"
  ]
}

TypeScript Configuration

For optimal TypeScript support with NativeWind:
tsconfig.json
{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    // Strict mode for better type safety
    "strict": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    
    // Module resolution
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "esModuleInterop": true,
    
    // Project settings
    "allowJs": true,
    "noEmit": true,
    "skipLibCheck": true,
    
    // Path aliases (optional)
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": [
    "**/*.ts",
    "**/*.tsx",
    "nativewind-env.d.ts",
    "global.css"
  ],
  "exclude": [
    "node_modules"
  ]
}

Custom Type Paths

If you use compilerOptions.types to manage type inclusion:
tsconfig.json
{
  "compilerOptions": {
    "types": [
      "react-native",
      "react-native-css/types"
    ]
  }
}
Disable auto-generation to avoid conflicts:
metro.config.js
module.exports = withNativewind(config, {
  disableTypeScriptGeneration: true,
});

Using TypeScript with NativeWind

Basic Usage

The className prop is now type-safe on all React Native components:
import { View, Text, Pressable } from "react-native";

export default function Example() {
  return (
    <View className="flex-1 bg-white p-4">
      <Text className="text-2xl font-bold text-gray-900">
        TypeScript + NativeWind
      </Text>
      <Pressable className="mt-4 bg-blue-500 px-6 py-3 rounded-lg">
        <Text className="text-white font-semibold">Press me</Text>
      </Pressable>
    </View>
  );
}

Typed Component Props

Create reusable components with typed props:
import { View, Text, type ViewProps } from "react-native";
import type { PropsWithChildren } from "react";

interface CardProps extends PropsWithChildren {
  title: string;
  variant?: "primary" | "secondary";
  className?: string;
}

export function Card({ title, variant = "primary", className, children }: CardProps) {
  const variantStyles = {
    primary: "bg-blue-500 border-blue-600",
    secondary: "bg-gray-500 border-gray-600",
  };

  return (
    <View className={`rounded-lg border-2 p-4 ${variantStyles[variant]} ${className || ""}`}>
      <Text className="text-xl font-bold text-white mb-2">{title}</Text>
      {children}
    </View>
  );
}
Usage:
<Card title="Welcome" variant="primary" className="mt-4">
  <Text className="text-white">Card content</Text>
</Card>

Styled Components Pattern

Create typed styled components using NativeWind’s styled function:
import { styled } from "nativewind";
import { View, Text, Pressable } from "react-native";

// Styled components with TypeScript
const Container = styled(View, "flex-1 bg-white p-4");
const Title = styled(Text, "text-2xl font-bold text-gray-900");
const Button = styled(Pressable, "bg-blue-500 px-6 py-3 rounded-lg active:bg-blue-600");
const ButtonText = styled(Text, "text-white font-semibold text-center");

export default function StyledExample() {
  return (
    <Container>
      <Title>Styled Components</Title>
      <Button onPress={() => console.log("Pressed")}>
        <ButtonText>Press me</ButtonText>
      </Button>
    </Container>
  );
}

CSS Variables with TypeScript

Use typed CSS variables with the vars helper:
import { View, Text } from "react-native";
import { vars, VariableContextProvider } from "nativewind";
import { useState } from "react";

interface ThemeVariables {
  "--primary-color": string;
  "--text-size": number;
}

export default function VariablesExample() {
  const [theme, setTheme] = useState<ThemeVariables>({
    "--primary-color": "#3b82f6",
    "--text-size": 16,
  });

  return (
    <VariableContextProvider value={vars(theme)}>
      <View className="flex-1 items-center justify-center" 
            style={{ backgroundColor: "var(--primary-color)" }}>
        <Text className="text-white" 
              style={{ fontSize: "var(--text-size)" }}>
          Themed Content
        </Text>
      </View>
    </VariableContextProvider>
  );
}

Type-Safe Hooks

NativeWind exports typed hooks from react-native-css:
import { useCssElement, useUnstableNativeVariable } from "nativewind";
import { View } from "react-native";

function TypedHooksExample() {
  // Type-safe CSS element hook
  const cssProps = useCssElement({
    className: "bg-blue-500 p-4 rounded-lg",
  });

  // Type-safe native variable hook
  const backgroundColor = useUnstableNativeVariable("--color-primary");

  return <View {...cssProps} />;
}

Advanced TypeScript Patterns

Conditional Class Names

Create a type-safe className utility:
type ClassValue = string | undefined | null | false;

function cn(...classes: ClassValue[]): string {
  return classes.filter(Boolean).join(" ");
}

// Usage
function Button({ primary, disabled }: { primary?: boolean; disabled?: boolean }) {
  return (
    <Pressable
      className={cn(
        "px-4 py-2 rounded",
        primary && "bg-blue-500",
        disabled && "opacity-50",
        !primary && "bg-gray-500"
      )}
    >
      <Text className="text-white">Button</Text>
    </Pressable>
  );
}

Variant Props Pattern

Create components with typed variants:
import { cva, type VariantProps } from "class-variance-authority";
import { Pressable, Text } from "react-native";
import type { PropsWithChildren } from "react";

const buttonVariants = cva(
  "rounded-lg font-semibold text-center",
  {
    variants: {
      variant: {
        primary: "bg-blue-500 text-white",
        secondary: "bg-gray-500 text-white",
        outline: "border-2 border-blue-500 text-blue-500",
      },
      size: {
        sm: "px-3 py-1.5 text-sm",
        md: "px-4 py-2 text-base",
        lg: "px-6 py-3 text-lg",
      },
    },
    defaultVariants: {
      variant: "primary",
      size: "md",
    },
  }
);

interface ButtonProps extends PropsWithChildren, VariantProps<typeof buttonVariants> {
  onPress?: () => void;
}

export function Button({ variant, size, onPress, children }: ButtonProps) {
  return (
    <Pressable className={buttonVariants({ variant, size })} onPress={onPress}>
      <Text className="text-inherit">{children}</Text>
    </Pressable>
  );
}

// Type-safe usage
<Button variant="outline" size="lg" onPress={() => {}}>
  Click me
</Button>

Generic Styled Components

Create generic typed components:
import { ComponentType } from "react";
import { View, Text, type ViewProps, type TextProps } from "react-native";

function createStyledComponent<T extends ViewProps | TextProps>(
  Component: ComponentType<T>,
  baseClassName: string
) {
  return (props: T & { className?: string }) => {
    const combinedClassName = `${baseClassName} ${props.className || ""}`.trim();
    return <Component {...props} className={combinedClassName} />;
  };
}

const Card = createStyledComponent(View, "bg-white rounded-lg shadow-md p-4");
const Heading = createStyledComponent(Text, "text-2xl font-bold");

Troubleshooting TypeScript

Problem: TypeScript shows error: Property 'className' does not exist on type 'ViewProps'Solution:
  1. Ensure nativewind-env.d.ts is generated and included in tsconfig.json
  2. Restart your TypeScript server in VSCode (Cmd/Ctrl + Shift + P > “TypeScript: Restart TS Server”)
  3. Clear Metro cache: expo start --clear
tsconfig.json
{
  "include": [
    "**/*.ts",
    "**/*.tsx",
    "nativewind-env.d.ts"  // Must be included
  ]
}
Problem: Changes to CSS not reflected in TypeScriptSolution:
  1. Restart Metro bundler
  2. Delete nativewind-env.d.ts and restart
  3. Run: expo start --clear
Problem: Multiple type declaration conflictsSolution: If using custom type paths, disable auto-generation:
metro.config.js
module.exports = withNativewind(config, {
  disableTypeScriptGeneration: true,
});
Then manually add types to your tsconfig.json:
{
  "compilerOptions": {
    "types": ["react-native-css/types"]
  }
}
Problem: styled function shows type errorsSolution: Ensure you’re importing from the correct package:
import { styled } from "nativewind"; // Correct
// not from "nativewind/styled" or other paths

Best Practices

1

Commit generated types

Always commit nativewind-env.d.ts to version control so team members have types immediately.
2

Use strict mode

Enable TypeScript strict mode for better type safety:
tsconfig.json
{
  "compilerOptions": {
    "strict": true
  }
}
3

Type your components

Always define prop interfaces for reusable components:
interface ComponentProps {
  title: string;
  className?: string; // Optional className override
}
4

Use const assertions

Use as const for better type inference with variants:
const variants = {
  primary: "bg-blue-500",
  secondary: "bg-gray-500",
} as const;

type Variant = keyof typeof variants;

Next Steps

Configuration

Learn about Metro and Babel configuration

Custom Styles

Create type-safe custom utilities

API Reference

Explore the styled function API

Troubleshooting

Common TypeScript issues