Skip to content
Back to skills

Rn Bootstrap

ASecurity

Scaffold a new Expo + React Native app from a PROJECT.md + PRD.md + DESIGN.md using the opinionated stack (Expo Router, TypeScript, NativeWind v4, Zustand, TanStack Query, Reanimated, expo-image, FlashList). Reads .workflow/meta.json with stack.framework="expo-rn" and phase in {prd_drafted, design_extracted}. Produces a running Expo app at the project root, sets phase to "scaffolded". Always idempotent: re-running detects existing files and skips. Use when dev-flow routes here from prd_drafte...

  • 6 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 19, 2026
developmenttypescriptpythongobashreactnextjsgitapifrontend

Works with

  • api

Security analysis

A100/100

Pro scans all 9 files and shows the line behind each finding

Scanned October 19, 2026

npx -y skills add lukedj78/dev-flow --skill rn-bootstrap --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Rn Bootstrap?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Rn Bootstrap
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lukedj78-rn-bootstrap/badge)](https://www.skillsdirectory.com/skills/lukedj78-rn-bootstrap)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: rn-bootstrap
description: 'Scaffold a new Expo + React Native app from a PROJECT.md + PRD.md + DESIGN.md using the opinionated stack (Expo Router, TypeScript, NativeWind v4, Zustand, TanStack Query, Reanimated, expo-image, FlashList). Reads .workflow/meta.json with stack.framework="expo-rn" and phase in {prd_drafted, design_extracted}. Produces a running Expo app at the project root, sets phase to "scaffolded". Always idempotent: re-running detects existing files and skips. Use when dev-flow routes here from prd_drafted+expo-rn, or the user says "scaffolda app expo from PRD", "create RN app from this PRD/DESIGN", "bootstrap expo app". Not for: adding screens (rn-add-screen, Wave 2), modules (rn-module-add, Wave 3), Next.js scaffolding (design-md-to-app — different stack).'
---

# rn-bootstrap — scaffold Expo + RN from PRD/DESIGN

## Contract

See `references/contracts.md` (vendored from `dev-flow`). Key facts:
- Reads `<project-root>/.workflow/meta.json#stack.framework` — must be `"expo-rn"`.
- Reads `<project-root>/.workflow/{PROJECT.md, PRD.md, DESIGN.md}` (the contract location — a `DESIGN.md` at the project root is accepted as a legacy fallback with a warning). DESIGN.md is required for tokens; if absent, uses defaults from `references/stack-defaults.md`.
- Writes the app to `<project-root>/` (the same directory).
- Sets `meta.json#phase = "scaffolded"` on success.
- Always idempotent: re-running detects existing `package.json` + `app/` and exits 0.

## When this skill applies

- Orchestrator routes here from `dev-flow` when `meta.json#stack.framework == "expo-rn"` and `meta.json#phase ∈ {prd_drafted, design_extracted}`.
- User says: "scaffolda app expo", "create RN app from PRD".

## Knowledge dependencies (read these first)

- `rn-fundamentals/SKILL.md` — confirms Expo SDK + New Architecture + TypeScript + npm.
- `rn-styling/references/nativewind-setup.md` — the 7-step NativeWind setup (this skill automates it).
- `rn-expo-router/references/concepts.md` — folder layout for `app/`.
- `references/i18n-rn.md` — **golden rule 2**: every frontend ships i18n from day one. Wire it during this scaffold (device-locale detection, `locales/{en,it}.json`, provider in `app/_layout.tsx`, persisted locale choice) and record `stack.i18n` + `stack.locales = ["en","it"]` in `meta.json`. Follow that reference — don't improvise the setup; adding i18n later touches every screen.

## Workflow

### Step 1 — Verify preconditions

Read `<project-root>/.workflow/meta.json`. Abort with a clear message if:
- file missing → "Run `dev-flow init_workflow.py` first."
- `stack.framework != "expo-rn"` → "This skill is for stack.framework=expo-rn. For Next.js use design-md-to-app."
- `phase ∉ {prd_drafted, design_extracted}` → "Expected phase prd_drafted or design_extracted, got X."

If `package.json` + `app/` already exist at project root: print "Already bootstrapped, nothing to do", set phase to scaffolded if not already, exit 0.

### Step 2 — Run create-expo-app

Run `scripts/init-expo-app.sh <project-root> <app-name>`. The app name comes from `meta.json#project_name` (or `PROJECT.md` title).

### Step 3 — Install opinionated stack

Run `scripts/install-stack.sh <project-root>`. Installs NativeWind v4 (with Tailwind 3.4 pin), Zustand, TanStack Query, Reanimated, RNGH, expo-image, FlashList.

### Step 4 — Wire NativeWind from DESIGN.md tokens

Run `npx tsx scripts/wire-nativewind.ts <project-root>`. Generates `tailwind.config.js`, `global.css`, `babel.config.js`, `metro.config.js`, `nativewind-env.d.ts` and `declarations.d.ts` (ambient `declare module` for `*.css` and the formatjs polyfills — TypeScript 6 rejects side-effect imports without a declaration, error TS2882). Reads tokens from a fenced ` ```json tokens ` block in `.workflow/DESIGN.md` (accepts LF + CRLF line endings).

### Step 5 — Generate folder structure + boilerplate

Create (only if absent — idempotent):
- `app/_layout.tsx` — Stack root, imports `../global.css`, renders `<Stack />`.
- `app/index.tsx` — hello-world screen using NativeWind classes (wraps in `SafeAreaView` from `react-native-safe-area-context`).
- `components/`, `lib/`, `store/`, `types/`, `assets/` — empty dirs with `.gitkeep`.
- `.env.example` — empty stub with `EXPO_PUBLIC_API_URL=`.
- `tsconfig.json` — extend `expo/tsconfig.base`, add `paths` for `@/*` **without `baseUrl`**: TypeScript 6 makes `baseUrl` a hard error (TS5101, removed in 7.0); `paths` resolve relative to the tsconfig on their own. Add `"types": ["jest"]` once `rn-write-tests` installs the test stack. Include `declarations.d.ts` (written by step 4) in `include` if the project narrows it.

Also patch `package.json`: `"main": "expo-router/entry"`, then delete the template's `index.ts` and `App.tsx`. The blank-typescript template (57 and 58 alike) ships `"main": "index.ts"`, which mounts `App.tsx` and never reads `app/` — `tsc` and `expo export` both pass on it while every route is dead code. `verify.ts` checks it.

Also patch `app.json`:
- `expo.scheme` — set to a kebab-case of `meta.json#project_name`.
- `expo.experiments.typedRoutes` — `true`.
- `expo.newArchEnabled` — **do not write it.** Always on since SDK 55; the key is ignored and absent from the config schema.
- `expo.plugins` — add `"expo-router"`.

### Step 6 — Verify (scripts/verify.ts)

Run `npx tsx scripts/verify.ts <project-root>`. If exit code != 0, do NOT bump phase. Report failures from `references/post-bootstrap-checklist.md`.

The last check runs `npx expo export --platform ios` into a temp dir: it is the only check that proves Metro can build the app, which `tsc` cannot — a missing expo-router peer (`expo-linking`, `expo-constants`, `react-native-screens`) type-checks clean and fails on the first `expo start`. It takes 30–60 s; `RN_BOOTSTRAP_SKIP_BUNDLE=1` skips it when the user is iterating on the scaffold. Then **run the empty app once on a simulator before adding anything** — an app that has never started has nowhere to debug from when a later package breaks it.

### Step 7 — Update meta.json + commit

Update `meta.json`:
- `stack`: merge `{ framework: "expo-rn", ui: "nativewind", auth: null, db: null, payments: null, deploy: null }` (existing keys preserved if already set).
- `stack_config`: merge `{ expo_sdk: "<X>", state_lib: "zustand", data_lib: "tanstack-query" }`.
- `phase`: set to `"scaffolded"`.
- `history`: append `{ skill: "rn-bootstrap", ran_at: <iso>, outputs: ["package.json", "app/", "tailwind.config.js", ...], phase_before: <prev>, phase_after: "scaffolded" }`.

If `<project-root>` is a git repo, create a commit: `chore: scaffold Expo + RN app via rn-bootstrap`.

## Updating meta.json (recommended pattern)

When this skill modifies state (artifact written, phase advanced, history appended), use the canonical script when available:

```bash
# Wherever dev-flow is installed (e.g. ~/.claude/skills/dev-flow/), invoke:
python3 .../dev-flow/scripts/update_meta.py <project-root> record-artifact \
    --path <relative-path> --produced-by '<this-skill-name>' [--derived-from <p1> <p2> ...]
python3 .../dev-flow/scripts/update_meta.py <project-root> set-phase <new_phase>
python3 .../dev-flow/scripts/update_meta.py <project-root> append-history \
    --skill '<this-skill-name>' --inputs '{...}' --outputs '{...}' --phase-after <new_phase>
```

The script enforces phase monotonicity, normalizes legacy kebab-case aliases (e.g. `module-added` → `module_added`), and writes the canonical sha256 + timestamp into `meta.json#artifacts`. **Fall back to direct JSON editing only if the script is not on PATH** (and warn the user).

## Folder structure rules (canonical — Expo Router hybrid)

**Non-negotiable Expo Router constraint**: `app/` is **file-based routing only**. Unlike Next.js App Router, Expo Router has no private-folder convention — there is no `_`-prefix skip rule, so every file placed under `app/` (aside from reserved names like `_layout.tsx`, `+not-found.tsx`) becomes a real route. A previous revision of this skeleton put `_components/` folders inside `app/`, copied from the Next.js convention — that was wrong for Expo (it creates ghost routes) and is corrected below. **Still true at `expo-router@57.0.16`**, read off the shipped `build/getRoutesCore.js`: the entire
ignore list is `+html`, `+native-intent`, `+api`, `+middleware` and whatever you pass as
`options.ignore` — **no underscore rule anywhere**. The only underscore-aware names in the build are
`_layout` and `_sitemap`. So `app/` still has zero tolerance for non-route files, and `_components/`
still becomes a ghost route. And it is **settled, not pending** — I wrote "the request is still open" here earlier today without
checking, and it is wrong: [expo/expo#44696](https://github.com/expo/expo/issues/44696) was closed
**`won't fix`** on 2026-06-01, because Expo Router wants components outside `app/` by design.
`options.ignore` is the escape hatch that exists today, and it is configuration rather than convention.

This skill scaffolds the canonical RN structure, hybrid model (spec: `docs/superpowers/specs/2026-06-06-folder-structure-refactor.md`, adapted for Expo Router):

```
app/                        # ROUTES ONLY — no components, no hooks, no utils
├── (auth)/                 # opt-in based on stack.route_groups
│   ├── _layout.tsx
│   └── sign-in.tsx
├── (app)/
│   ├── _layout.tsx         # redirect to (auth) if !session
│   ├── (tabs)/             # opt-in based on stack.route_groups
│   │   ├── _layout.tsx
│   │   ├── feed/
│   │   └── profile/
│   └── settings.tsx
└── _layout.tsx             # root: GestureHandlerRootView + ThemeProvider

components/                 # ALL non-route UI lives here, outside app/
├── ui/                     # NativeWind primitives
├── theme/                  # ThemeProvider, useThemeColor
├── shared/                 # L2 per dominio (created empty)
└── <feature>/              # L0/L1 screen-private components (e.g. components/feed/, components/profile/)
                             # created on demand by rn-add-screen, not scaffolded empty here

lib/                        # api, supabase, secure-store, utils
store/                      # Zustand cross-feature (auth-store, app-preferences)
hooks/                      # RN-specific (useColorScheme, useKeyboard)
assets/                     # images/, fonts/
```

No `src/` prefix: `app/`, `components/`, `lib/` etc. stay at the project root — matches the default `create-expo-app` templates (which don't use `src/`) and keeps `rn-add-screen` / `promote-component` consistent with this scaffold.

Read `meta.json#stack.route_groups` to decide which route groups to scaffold. Empty array → flat routing (rare for mobile).

Components follow Rule of Three for promotion (L0 → L1 → L2), targeting `components/<feature>/` (L0/L1) and `components/shared/<dominio>/` (L2) — **never** `app/<route>/_components/`. See `rn-add-screen` for screen-level details and `promote-component` for the promotion mechanics.

## Sources

- Course: codewithbeto.dev/rnCourse — free lessons 5-6 (Creating Your First App, Project Structure).
- Official: https://docs.expo.dev/get-started/create-a-project/
- Official: https://www.nativewind.dev/docs/getting-started/installation — ⚠️ the old `nativewind.dev/v4/getting-started/expo-router` URL **404s** as of 2026-08-26 (the site dropped the `/v4/` prefix and no longer has an Expo-Router-specific page); Expo Router setup is folded into Installation.

Files in this skill

  • SKILL.md11.4 KB
  • references/contracts.md80.5 KB
  • references/i18n-rn.md14.8 KB
  • references/post-bootstrap-checklist.md3.1 KB
  • references/stack-defaults.md6 KB
  • scripts/init-expo-app.sh4.1 KB
  • scripts/install-stack.sh2.3 KB
  • scripts/verify.ts5.8 KB
  • scripts/wire-nativewind.ts6.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.