ReadyNative

StyleSheet (plain RN)

Set up StyleSheet (plain RN) for UI in an Expo app with ReadyNative: no style engine, tokens + StyleSheet.create · Expo Go OK

Expo Go: yes

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

The "no framework" stack: every primitive in @/components/ui is plain React Native styled with StyleSheet.create from src/theme/tokens.ts. Pick it when you don't want a style engine at all - nothing to compile, no className, no dependency beyond core. It's also the clearest read of the contract: src/components/ui/ (src/ui/stylesheet/ before setup) is the reference implementation. Expo Go works.

Setup

bun run setup --ui stylesheet

That's it. Setup writes a plain babel.config.js and metro.config.js (Expo defaults plus the .wasm asset extension), every primitive under src/components/ui/, registers ThemeProvider (order 50), and adds src/components/ui/jest-setup.ts, which stubs @expo/ui native views under jest. Edit src/theme/tokens.ts and the next render picks it up - there's no generate step.

Usage

Screens use the primitives:

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

<Screen scroll>
  <Card p={4}>
    <Text variant="title">Today</Text>
  </Card>
</Screen>;

Your own components use useStyles - StyleSheet.create bound to the current theme, memoised per mode. Declare the factory at module scope:

import { useStyles, useTheme } from "@/components/ui";
import type { Theme } from "@/components/ui";

const panelStyles = (t: Theme) => ({
  root: { backgroundColor: t.colors.card, borderRadius: t.radius.md, padding: t.space[4] },
});

export function Panel() {
  const styles = useStyles(panelStyles);
  const { colors } = useTheme(); // { mode, colors, space, radius } for inline colours
  return <View style={styles.root} />;
}

Styles that never read the theme can use StyleSheet.create directly.

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

Gotchas

  • @expo/ui universal components (Host, Switch) are bundled in Expo Go on SDK 57.
  • A mode change re-renders the context and every primitive rebuilds its styles - there's no engine to notify. ForcedMode overrides the context for a subtree.
  • Switch is a native SwiftUI / Compose control inside Host, measured natively, so it can lay out one frame after its siblings. Swap in react-native's Switch in switch.tsx if that bothers you - the label row is already there.
  • Text variants approximate NativeWind 5's rnr-text scale (title = h3, heading = h4, subtitle = lead, caption = muted, label = small); code is fontFamily: "monospace" (Menlo on iOS).
  • Web is best-effort: @expo/ui's web Switch is a styled checkbox from react-native-web.

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

Install

bun run setup --ui stylesheet

Module id: ui/stylesheet.

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 drops the NativeWind transform cache.

Files

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

On this page

Get ReadyNative