ReadyNative

NativeWind 4

Set up NativeWind 4 for UI in an Expo app with ReadyNative: Stable · Tailwind 3 classes + RNR components behind @/ui · Expo Go OK

Expo Go: yes

Runs in Expo Go; no dev build needed for this module.

The default UI stack: stable NativeWind 4.2 (nativewind@4.2.7) on Tailwind 3, with React Native Reusables (RNR) text / button as the kit in src/components/ui/ (src/ui/nativewind4/ before setup). Every preset and the committed tree use it. Pick nativewind5 (RC · Tailwind 4) instead only if you want Tailwind 4 before NativeWind 5 is released. Pull components straight from the RNR registry - components.json already points at @/components/ui. Expo Go works.

Setup

bun run setup --ui nativewind4
  1. Set your colours in src/theme/tokens.ts (hex).
  2. bun run gen:theme - converts them to HSL triples and rewrites src/global.css and tailwind.config.js.
  3. bun run start -- -c - once, so Metro picks up the NativeWind transform.

Setup writes babel.config.js (jsxImportSource: "nativewind" + nativewind/babel), metro.config.js (withNativeWind, inlineRem: 16), tailwind.config.js (darkMode: "class", colours as hsl(var(--x) / <alpha-value>)), src/global.css, nativewind-env.d.ts and components.json, and registers ThemeProvider (order 50).

8-digit hex loses its alpha

HSL triples can't carry alpha, so a token like the dark border #ffffff1a is composited onto that mode's background by the generator. The generated CSS says so inline.

Usage

Screens use the primitives:

import { Box, Button, Screen, Text } from "@/components/ui";

<Screen>
  <Box p={4} gap={2}>
    <Text variant="heading">Settings</Text>
    <Button onPress={signOut}>Sign out</Button>
  </Box>
</Screen>;

Your own components use className for static classes and useTheme() for anything token-driven:

import { useTheme } from "@/components/ui";

const { colors, mode } = useTheme(); // resolved-mode hex, for inline styles

To add a primitive to the kit: create it in src/components/ui/, add its prop type to src/components/ui/types.ts, and export it from src/components/ui/index.ts.

Gotchas

  • darkMode: "class" means NativeWind never follows the OS by itself. ThemeProvider calls colorScheme.set(mode) with the stored preference - Appearance.setColorScheme on native, the dark class on <html> on web.
  • ForcedMode (the dev catalog) wraps a subtree in a View with style={vars(hslTriples)} because NativeWind 4 has no VariableContextProvider. Inside it, dark: utilities still follow the global scheme, and it adds one layout node.
  • nativewind #1834 (open): LogBox breaks on RN 0.86 with nativewind/babel in the preset list (fix PR #1840 unmerged). Dev-only; release bundles and expo export are fine. If the red box misbehaves, drop "nativewind/babel" from babel.config.js - only React.createElement-style code loses className.
  • Web is best-effort: colorScheme.set throws outside a browser (guarded) and SSR export of .dark:root is untested.
  • Before setup, or with --keep-modules, bun run setup --ui <other> swaps the adapter and config. Screens don't change.

Remove it

Every app needs a ui option, so there is no none to fall back to - you switch instead. While modules/ exists (a tree set up with --keep-modules), one call does it:

bun run setup --ui nativewind5 --yes --keep-modules

A finalized tree can't switch: clone a fresh copy of your tier repo, run setup there with the new option and port your code across - see Can I change a module after setup?.

Reference

Everything below is generated from modules/ui/nativewind4/module.json - the same file bun run setup reads, so it is what actually lands in your repo.

Install

bun run setup --ui nativewind4

Module id: ui/nativewind4 (the default for this category).

Dependencies

PackageVersionKind
@rn-primitives/slot^1.5.2dependency
class-variance-authority^0.7.1dependency
clsx^2.1.1dependency
nativewind4.2.7dependency
tailwind-merge^3.7.0dependency
tailwindcss-animate^1.0.7dependency
tailwindcss^3.4.19devDependency

Providers

Rendered in src/providers.tsx (lower order = outermost):

OrderProviderFrom
50ThemeProvider as UiThemeProvider@/ui

After setup

  1. Run bun run start -- -c once so Metro picks up the NativeWind transform.

Files

31 files copied to the project root
  • babel.config.js
  • metro.config.js
  • tailwind.config.js
  • components.json
  • nativewind-env.d.ts
  • src/global.css
  • src/ui/nativewind4/box.tsx
  • src/ui/nativewind4/button.tsx
  • src/ui/nativewind4/card.tsx
  • src/ui/nativewind4/cn.ts
  • src/ui/nativewind4/divider.tsx
  • src/ui/nativewind4/empty-state.tsx
  • src/ui/nativewind4/error-state.tsx
  • src/ui/nativewind4/hsl.ts
  • src/ui/nativewind4/icon.tsx
  • src/ui/nativewind4/index.ts
  • src/ui/nativewind4/jest-setup.ts
  • src/ui/nativewind4/input.tsx
  • src/ui/nativewind4/list.tsx
  • src/ui/nativewind4/loading.tsx
  • src/ui/nativewind4/pressable.tsx
  • src/ui/nativewind4/rnr-button.tsx
  • src/ui/nativewind4/rnr-text.tsx
  • src/ui/nativewind4/row.tsx
  • src/ui/nativewind4/screen.tsx
  • src/ui/nativewind4/sheet.tsx
  • src/ui/nativewind4/skeleton.tsx
  • src/ui/nativewind4/switch.tsx
  • src/ui/nativewind4/text.tsx
  • src/ui/nativewind4/theme-provider.tsx
  • src/ui/nativewind4/toast.tsx

On this page

Get ReadyNative