ReadyNative

Scripts

Every command in package.json - what it does, which flags it takes, and which ones survive setup.

All CLIs are thin files in scripts/; the logic lives in scripts/lib/*.ts. They run under plain node (Node 22.18+ strips TypeScript types natively), so bun run, npm run, pnpm run and yarn run are interchangeable - see Package managers. Every script accepts --root <dir> (default: cwd), which is how the unit tests run them against fixture trees.

Scripts fall into two groups. Stays after setup - what lives in the tree you ship: doctor, gen, gen:theme, gen:links, gen:privacy, gen:assets, gen:graph, test:e2e, plus typecheck / lint / format / test. Module system - leaves with modules/ unless you pass --keep-modules: setup itself (it stays as a one-line stub that says it already ran) and clean.

Setup and health

bun run setup

This is the one that assembles your tree.

bun run setup [--preset minimal|default|saas] [--<category> <option>]…
              [--yes] [--dry-run] [--no-install] [--keep-modules]
              [--with-examples] [--pm bun|pnpm|yarn|npm] [--root <dir>]

It copies the selected modules into the tree, generates the providers, env and .readynative.json, patches package.json / tsconfig.json, strips the demo content (unless --with-examples) and, unless --keep-modules, finalizes the tree: the kit moves to src/components/ui and scripts/ is trimmed to the ones marked "stays" above. Every step is in How setup works. On the Free tier none of this exists: the tree ships already set up, without scripts/.

FlagEffect
--preset <name>Start from minimal, default or saas
--<category> <opt>Override one category, e.g. --data none, --ui tamagui, --auth supabase
--yesNo prompts; take preset/flags (or the previous .readynative.json)
--dry-runPrint the plan (selection, package manager, Expo Go verdict, dependency delta, files) and change nothing
--no-installSkip the install and expo install after applying
--keep-modulesDo not delete modules/ afterwards - required to run setup again with another selection
--with-examplesKeep the weather demo, the example screens and the UI catalog (default: strip them)
--pm <name>Force bun, pnpm, yarn or npm instead of the detected one
--helpUsage plus every category and option

It's idempotent: the same selection twice produces no diff. Unknown options, unmet requires and conflicts are errors raised before anything is written. When it finishes, it prints the post-setup steps of every selected service, one guide link each, and a https://readynative.app/docs/?stack=… URL that opens these docs pre-filtered to your selection.

bun run doctor

The post-setup checklist, with a non-zero exit on failures: .readynative.json, the env keys your modules need, placeholders in readynative.config.ts, eas whoami, the native toolchains when a module needs them, your package manager, and the bun and Node versions. --store turns the "Before you ship" warnings into failures. It closes with the ?stack= docs link for your selection. Row by row: Doctor.

Generators

All of these stay in your tree after setup. gen module is the exception in practice: it scaffolds into modules/, so it's useful only while that folder exists.

ScriptWhat it does
bun run gen:theme [--check]src/theme/tokens.ts → src/global.css (nativewind5), src/global.css + tailwind.config.js (nativewind4), tamagui.config.ts; writes the active stack's file at the root (and, while modules/ exists, every stack's copy under modules/ui/<stack>/files/). --check exits 1 on drift. See Styling → Theme tokens and gen:theme
bun run gen:links [--check]links in readynative.config.ts → public/.well-known/apple-app-site-association + assetlinks.json for the prod/preview/dev ids
bun run gen:privacy [--owner "<entity>"]readynative.config.ts + .readynative.json (the selected modules' privacy declarations) → docs/privacy-policy.md, a privacy-policy draft matching what the build ships. --stdout prints instead. Re-run after setup changes a module; not legal advice.
bun run gen:assets [--source <png>]assets/brand/icon.png + brand.primary → icon, adaptive icon layers, splash icon, favicon (runs under Node)
bun run gen:graph [--check]The tree → docs/graph.json + docs/ARCHITECTURE.md: modules, providers, lib shims, routes, screens, features, stores, hooks, env keys, services and scripts, with the imports / provides / reads / renders / configures edges between them. --check exits 1 when the committed files are stale - run it before you commit, or add it to your own CI
bun run gen screen <name>src/screens/<name>/<name>-screen.tsx + the src/app/<name>.tsx route. --tab [--icon <sf-symbol>] [--icon-fallback <material>] [--title "<Title>"] routes it under src/app/(tabs)/ and inserts a <NativeTabs.Trigger> entry (SF Symbol + Material icon, localised label) at the {/* gen:tabs */} marker in src/app/(tabs)/_layout.tsx (plus the title key in src/locales/en.json); --modal writes src/app/<name>/{_layout,index}.tsx with presentation: "modal" and registers it at the {/* gen:stack */} marker in src/app/_layout.tsx. Mutually exclusive; a missing marker prints the block to paste
bun run gen module <category> <option>modules/<category>/<option>/{module.json,files/,docs.md} scaffold - then fill module.json and docs.md

Run gen:graph whenever you add a route, a lib shim, a provider or an env key - it's how agents working in your repo find their way around. To extend it without touching the script, see Conventions → Extending the graph.

Housekeeping

This one exists only while modules/ does: before setup, or after setup --keep-modules. Finalizing removes it, and the two example apps never had it. In a finalized tree, delete the files it lists by hand.

ScriptWhat it does
bun run clean [--no-examples] [--yes]Remove demo content - what setup already does unless --with-examples: src/app/_catalog.tsx and its Settings row, placeholder assets, plus the weather demo (src/app/(tabs)/cities/, src/screens/weather/, src/lib/weather/, src/stores/weather.ts, src/hooks/use-forecast.ts, its tab), src/app/examples/, src/screens/examples/, src/examples.ts, the example hook/store (and their tests) and the Examples row in Settings. The home screen becomes a plain welcome and the Weather tab turns back into Home. --no-examples keeps the weather demo and the examples tab

Checks

The four I run before every commit, plus the end-to-end runner.

ScriptCommand
bun run typechecktsc --noEmit
bun run lintexpo lint (ESLint flat config)
bun run lint:fixexpo lint --fix (auto-fixable rules + Prettier formatting)
bun run formatprettier --write .
bun run testjest - present only with testing=jest; testing=none removes the script and every __tests__
bun run test:e2enode scripts/e2e.ts - runs the Maestro flows in .maestro/flows against an installed dev build, with APP_ID / APP_NAME read from readynative.config.ts. --variant dev|preview|prod picks the ids (default dev), --dry-run prints the maestro command

Your tree ships no git hooks and no CI: nothing runs on git commit or push. Run bun run typecheck, bun run lint, bun run test and bun run gen:graph --check yourself, or wire them into your own CI. For checks before each commit, add a hook runner yourself - for example bun add -d lefthook, a lefthook.yml with a pre-commit job, then bunx lefthook install.

Expo

bun run start / ios / android / web wrap expo start, expo run:ios, expo run:android and expo start --web. start is expo start --go when every selected module runs in Expo Go and expo start --dev-client otherwise (the tree always carries expo-dev-client, so a bare expo start would wait for a dev build); bun run start:dev always targets the dev build.

There's no release script in your tree: a release is npm version <patch|minor|major> plus git push --follow-tags, then an EAS production build - see Ship to TestFlight → Automate it.

Module-provided scripts

A module may add scripts through package.scripts in its module.json; they disappear when the module isn't selected (test from testing/jest and i18n:extract from i18n/lingui are two). The one with its own file is push:test, from push/expo-notifications Pro: a tree without that module has no push:test and no scripts/push-test.ts. It sends one notification through the Expo push service to a device token (read it from push.usePushToken().token, or from /examples/push when you kept the examples):

bun run push:test "ExponentPushToken[…]" "Title" "Body"

It runs scripts/push-test.ts on plain node, so it works with any package manager (npm run push:test -- … on npm), and it survives finalizing: any scripts/ file a package.json script runs is kept. The notification deep-links to /examples/push, which only exists with --with-examples; without the examples, pass --url / (or any route you have). Remote push needs a dev build on a physical device - see Add push notifications.

On this page

Get ReadyNative