ReadyNative

Unistyles 3

Set up Unistyles 3 for UI in an Expo app with ReadyNative: C++ StyleSheet with themes/breakpoints behind @/ui · dev build required (no Expo Go)

Expo Go: no - dev build required

Ships native code. Make a dev build with bunx expo run:ios / bunx expo run:android (or bunx eas-cli build --profile development).

A C++/Nitro style engine: StyleSheet.create((theme) => …) styles are bound to the shadow tree and re-themed without a React re-render, as the kit in src/components/ui/ (src/ui/unistyles/ before setup). Pick it when theme switching and breakpoints need to be as cheap as possible and you're already on a dev build - it's the one UI option that can't run in Expo Go.

Setup

bun run setup --ui unistyles
  1. Make a dev build: npx expo run:ios / npx expo run:android (or eas build --profile development). Unistyles is a native module.
  2. Set your colours in src/theme/tokens.ts - src/unistyles.ts builds both themes from them, no generate step.
  3. bun run start -- -c - once, since the Babel plugin and the entry file both changed.

Setup installs react-native-unistyles ^3.3.0, react-native-nitro-modules 0.37.1 (exact - Unistyles pins the Nitro ABI) and react-native-edge-to-edge ^1.8.1 with its config plugin, and writes:

  • babel.config.js - babel-preset-expo plus ["react-native-unistyles/plugin", { root: "src" }].
  • index.ts at the root plus package.json main: "index.ts", so src/unistyles.ts runs StyleSheet.configure before expo-router/entry creates any stylesheet. Switching stacks restores main: "expo-router/entry".
  • src/unistyles.ts - the light / dark themes, breakpoints xs sm md lg xl, settings.adaptiveThemes: true, and the UnistylesThemes / UnistylesBreakpoints augmentations.
  • src/app/+html.tsx (web shell), every primitive under src/components/ui/, and src/components/ui/jest-setup.ts.

Usage

Screens use the primitives:

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

<Screen>
  <Box p={4} gap={2}>
    <Text variant="title">Library</Text>
  </Box>
</Screen>;

Your own components write theme functions:

import { StyleSheet } from "react-native-unistyles";

const styles = StyleSheet.create((theme) => ({
  card: { backgroundColor: theme.colors.card, padding: theme.space[4] },
}));

variants plus styles.useVariants({ tone: "danger" }) for prop-driven looks; useTheme() for inline token colours. Theme mode is the usual setThemeMode("system" | "light" | "dark") from @/components/ui - ThemeProvider mirrors it into the runtime (system turns adaptive themes on, an explicit mode turns them off, then setTheme(mode)).

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

Gotchas

  • No Expo Go. bun run doctor flags it in its devBuild check. expo export still works - it's only the JS bundle.
  • setTheme throws while adaptive themes are on, which is why ThemeProvider calls setAdaptiveThemes(false) first.
  • The official jest mock strips variants and resolves against the first registered theme (light), so tests see base styles only.
  • react-native-edge-to-edge is an optional peer of Unistyles ≥ 3.1 (Expo 54+ is edge-to-edge itself), installed for ecosystem compatibility. expo install can't add its plugin to a dynamic app.config.ts, so the module declares it in app.expo.plugins.
  • Native is the target; the web export is best-effort.

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 nativewind4 --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/unistyles/module.json - the same file bun run setup reads, so it is what actually lands in your repo.

Install

bun run setup --ui unistyles

Module id: ui/unistyles.

Dependencies

PackageVersionKind
react-native-edge-to-edge^1.8.1dependency
react-native-nitro-modules0.37.1dependency
react-native-unistyles^3.3.0dependency

Config plugins

Merged into app.config.ts through .readynative.json (modules.app):

  • react-native-edge-to-edge

Providers

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

OrderProviderFrom
50ThemeProvider as UiThemeProvider@/ui

Doctor checks

  • dev build toolchain (Xcode / Android SDK)

After setup

  1. Unistyles is a native (Nitro) module: make a dev build with npx expo run:ios / npx expo run:android (or eas build --profile development). Expo Go will not load it.
  2. Run bun run start -- -c once so Metro picks up the Unistyles Babel plugin and the new entry file.

Files

26 files copied to the project root
  • babel.config.js
  • index.ts
  • metro.config.js
  • src/app/+html.tsx
  • src/ui/unistyles/box.tsx
  • src/ui/unistyles/button.tsx
  • src/ui/unistyles/card.tsx
  • src/ui/unistyles/divider.tsx
  • src/ui/unistyles/empty-state.tsx
  • src/ui/unistyles/error-state.tsx
  • src/ui/unistyles/icon.tsx
  • src/ui/unistyles/index.ts
  • src/ui/unistyles/input.tsx
  • src/ui/unistyles/jest-setup.ts
  • src/ui/unistyles/list.tsx
  • src/ui/unistyles/loading.tsx
  • src/ui/unistyles/pressable.tsx
  • src/ui/unistyles/row.tsx
  • src/ui/unistyles/screen.tsx
  • src/ui/unistyles/sheet.tsx
  • src/ui/unistyles/skeleton.tsx
  • src/ui/unistyles/switch.tsx
  • src/ui/unistyles/text.tsx
  • src/ui/unistyles/theme-provider.tsx
  • src/ui/unistyles/toast.tsx
  • src/unistyles.ts

On this page

Get ReadyNative