ReadyNative

UI contract

Screens import primitives only from @/ui; the stack behind it is a swappable adapter typed by UiKit.

This is the reference for src/ui/contract.ts, the one contract every UI stack implements. If you want to style a screen rather than read the spec, start with Styling.

It applies to the tree before bun run setup, or after bun run setup --keep-modules. A finalized tree has no contract: the kit sits in src/components/ui, its prop types in src/components/ui/types.ts, and there's no UiKit left to satisfy.

Screens, components and routes import UI only from @/ui, so switching from NativeWind to Tamagui (or to plain StyleSheet) changes the adapter under src/ui/<stack>/, not the app.

src/ui/index.ts (generated by setup)
export * from "./contract";
export * from "./theme-mode";

export * from "./nativewind4"; // the active stack

Primitives

UiKit is the runtime export list every src/ui/<stack>/index.ts must provide: the primitives in Styling → The primitives, which also covers list screens. Theme mode (useThemeMode, setThemeMode, useResolvedMode) is re-exported from src/ui/theme-mode.ts, and Box's short align / justify aliases are FlexAlias in the contract - adapters map them.

Rules

  • Props are token-typed. Spacing props take SpaceToken (0 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 12 | 16), colours take ColorToken, radii take RadiusToken - never raw numbers or hex strings.
  • style is the only escape hatch. Every primitive accepts style?: StyleProp<ViewStyle> (or TextStyle). There is deliberately no className on the contract; Tailwind classes are an implementation detail of the NativeWind adapters and do not exist for Tamagui, Unistyles or StyleSheet.
  • The contract file imports nothing from a stack. Only React and react-native types.
  • ESLint enforces it (eslint.config.js, for src/screens/** and src/app/**): View, Text, StyleSheet, Pressable, TextInput, Switch from react-native are restricted; so are @/components/ui/*, @/ui/<adapter> (only @/ui and @/ui/contract are public), @/theme/*, nativewind, react-native-css, react-native-reanimated, and importing readynative.config directly (use @/lib/readynative).

Tokens

The token sets and bun run gen:theme work the same in every tree - see Styling → Theme tokens and gen:theme.

Adding a UI stack adapter

This needs modules/, so it works in a tree set up with --keep-modules.

  1. bun run gen module ui <stack> scaffolds modules/ui/<stack>/{module.json,files/,docs.md}.

  2. Implement every primitive under files/src/ui/<stack>/ and finish index.ts with:

    src/ui/<stack>/index.ts
    const kit = {
      ThemeProvider,
      useTheme,
      Screen,
      Box,
      Text,
      Button,
      Pressable,
      Card,
      Row,
      List,
      Input,
      Switch,
      Divider,
      Icon,
      Skeleton,
      Loading,
      EmptyState,
      ErrorState,
      ToastHost,
      toast,
    } satisfies UiKit;
    
    export default kit;

    satisfies UiKit is what makes a missing or mistyped primitive a compile error.

  3. Declare the theme provider in module.json so setup nests it at order 50: { "import": "ThemeProvider", "as": "UiThemeProvider", "from": "@/ui", "order": 50 }, and list every file under files[].

  4. If the stack needs a generated theme file, add a renderer in scripts/lib/gen-theme*.ts and put its id in module.json.gen.

  5. Apply it with bun run setup --ui <stack> --yes --keep-modules, then run bun run typecheck, bun run lint, bun run test and bunx expo export -p ios -p android. Run setup a second time: it should change nothing. The stylesheet adapter is the reference implementation of the contract - copy its structure.

On this page

Get ReadyNative