# Rn Add Screen

> Use to add a new screen to an existing Expo + RN app: from a description, a wireframe, or a screenshot, generate the route file in app/ (Expo Router file-based), wire up data fetching via TanStack Query if needed, apply NativeWind classes from the project DESIGN.md tokens, and respect the scaffolded folder layout. Reads .workflow/meta.json with stack.framework="expo-rn" and phase ≥ "scaffolded". Use when dev-flow routes here from scaffolded+expo-rn, or the user says "add a login screen", "create a profile screen from this screenshot", "aggiungi una schermata X". Not for: scaffolding the app (rn-bootstrap), adding backend modules (rn-module-add Wave 3), pure styling changes to an existing screen (rn-styling).

- Skill: `lukedj78/rn-add-screen` (Agent Skill, multi-file: 5 files)
- Install (CLI): `npx skillmds@latest add lukedj78/rn-add-screen`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lukedj78/rn-add-screen/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: lukedj78 (https://skillmd.com/u/lukedj78)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lukedj78/rn-add-screen

---


# rn-add-screen — add a new screen to a scaffolded RN/Expo app

## Contract

See `references/contracts.md` (vendored from `dev-flow`). Key facts:
- Reads `<project-root>/.workflow/meta.json#stack.framework` — must be `"expo-rn"`.
- Requires `meta.json#phase ≥ "scaffolded"` (run `rn-bootstrap` first).
- Reads `.workflow/DESIGN.md` (the contract location) for the design tokens; the NativeWind classes it emits must exist in the `tailwind.config.js` that `rn-bootstrap` generated from it.
- Writes ONE new file under `<project-root>/app/...` per call — **routes only**: Expo Router treats every file under `app/` as a route, so nothing else may live there. Optional: screen-private components under `<project-root>/components/<feature>/` (L0, OUTSIDE `app/` — see Folder structure rules below), hooks under `<project-root>/lib/queries/`.
- Sets `meta.json#phase = "page_generated"` after the first screen, then leaves it (subsequent screens are still `page_generated`).
- Always idempotent: re-adding the same route detects the existing file and reports.

## When this skill applies

- Phase is `scaffolded` or `page_generated`.
- The user describes a screen: name, content, behavior. Or provides a screenshot/wireframe to translate.
- Orchestrator routes here from `dev-flow`.

## Knowledge dependencies (read these first)

- `rn-fundamentals/SKILL.md` — file layout, modern primitives.
- `rn-styling/references/patterns.md` — root screen pattern, dark mode, FlashList.
- `rn-expo-router/references/concepts.md` — file-based routing rules.
- `rn-expo-router/references/patterns.md` — modal vs push, search params, auth gates.
- `rn-data-fetching/references/patterns.md` — if the screen fetches data, use the patterns there.
- `rn-components-apis/references/decision-tree.md` — which primitive (FlashList vs ScrollView, Pressable, etc.).

## Workflow

### Step 1 — Verify preconditions

Read `.workflow/meta.json`. Abort with clear message if:
- `stack.framework != "expo-rn"` → "Wrong stack."
- `phase < "scaffolded"` → "Run rn-bootstrap first."

### Step 2 — Understand the screen

Gather from user (one round-trip max — collect everything at once):

- **Route path** in Expo Router convention: `/profile/[id]` → `app/profile/[id].tsx`. Group? `(auth)/sign-in`? Modal? See `references/screen-patterns.md` for the mapping.
- **Top-level layout**: scroll vs FlashList vs form. Modal or full-screen.
- **Data**: does it need a query? A mutation? Static?
- **Navigation in**: how does the user reach it (link from another screen, push, modal, deep link)?
- **Navigation out**: what does it do on success / back?

If a screenshot is provided, infer the above and confirm with the user before generating.

### Step 3 — Pick the right scaffold template

See `references/screen-patterns.md` for canonical templates:
- List screen (FlashList + TanStack Query)
- Detail screen (typed search params + query)
- Form screen (KeyboardAvoidingView + controlled inputs + mutation)
- Modal screen (presentation: "modal", dismiss button)
- Auth-gated screen (inside `(app)/` group)

Pick ONE template; do not mix unless the screen is genuinely hybrid.

### Step 4 — Generate the file

Write to `<project-root>/app/<route>.tsx`. The file MUST:
- Use `SafeAreaView` from `react-native-safe-area-context` at root.
- Use NativeWind classes from existing `tailwind.config.js` tokens (no magic colors).
- Use `Pressable`, `expo-image`, `FlashList` as appropriate.
- If using data, use TanStack Query with a centralized query key (add to `lib/query-keys.ts` if it doesn't exist).
- If using a form, use controlled state + a `useMutation` hook for submit.

### Step 5 — Update related files (only if necessary)

- If the screen uses a NEW component, write it under `components/<feature>/` (L0, e.g. `components/posts/PostCard.tsx` for a `/posts` route) — NEVER under `app/<route>/_components/` (Expo Router has no private-folder convention; every file under `app/` becomes a route — see the Folder structure rules below). Promote to `components/shared/<dominio>/` (L2) only per the Rule of Three.
- If the screen uses a NEW query/mutation, write the hook under `<project-root>/lib/queries/` or `lib/mutations/`.
- If the screen is reachable from another screen, add a `<Link>` there ONLY IF the user explicitly asks.

### Step 6 — Verify

Run:
- `npx tsc --noEmit` from project root → must pass.
- `npx expo-router-typegen` (if available) so typed routes refresh.

If typing fails, fix and re-verify before reporting done.

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

- `meta.json#phase`: if currently `"scaffolded"`, set to `"page_generated"`. Otherwise leave.
- `meta.json#history`: append `{ skill: "rn-add-screen", ran_at: <iso>, outputs: [<file paths>], phase_before: <prev>, phase_after: <current> }`.
- If git repo: `git add` the new files + `git commit -m "feat(<route>): add <screen-name> screen"`.

## Common anti-patterns (NEVER do)

- ❌ Add layout configuration (`<Tabs.Screen>`, `<Stack.Screen>`) inside the screen file. Layout lives in `_layout.tsx`.
- ❌ Rewrite an existing screen unless the user explicitly says "rewrite".
- ❌ Hardcode colors or spacing in the new file — use Tailwind tokens.
- ❌ Use `Image` from `react-native` — `expo-image`.
- ❌ Use `FlatList` for a long list — `FlashList`.
- ❌ Use `fetch + useEffect` for production data — TanStack Query.
- ❌ Touch unrelated files (`tailwind.config.js`, `app.json`, other routes).

## 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 convention for "private", non-routable folders — there is no `_`-prefix skip rule. Every `.tsx`/`.ts` file placed under `app/` (aside from a few reserved names like `_layout.tsx`, `+not-found.tsx`) is registered as a real route. Putting a component at `app/<route>/_components/PostCard.tsx` creates a ghost route at that path, not a private folder.

**This is Expo Router's settled design, not a gap.** The request ([expo/expo#44696](https://github.com/expo/expo/issues/44696)) was closed **`won't fix`** on 2026-06-01: the maintainers *"explicitly want people to fall into a pit of success where screens / UI / business logic … are moved into a separate folder"*, and consider filename conventions brittle because `_`, `+` and `-` are all legal in pathnames. Confirmed mechanically at `expo-router@57.0.16`: the route scanner has no underscore rule. So assume no private folders inside `app/` — permanently, not provisionally. Detail in `promote-component/references/colocation-rules.md`.

Given that constraint, the rule for this skill is:

- **`app/` = routes only.** No components, no hooks, no utils — ever.
- **Components go OUTSIDE `app/`, in `components/<feature>/<Component>.tsx`** (kebab-case feature folder named after the screen/domain, e.g. `components/posts/PostCard.tsx`; complex/compound components get their own subfolder). This is the mobile equivalent of what `_components/` does on web.
- No `src/` prefix: keep `app/`, `components/`, `lib/` at the project root — consistent with `rn-bootstrap`'s scaffold and the default `create-expo-app` templates (which don't use `src/`).
- The **model** (Rule of Three, L0 → L1 → L2 promotion ladder) is unchanged from the general dev-flow contract — only the mobile **target paths** differ from the web ones documented in `references/contracts.md`:

  | Level | Web target (`_components/` valid) | Mobile target (this skill) |
  |---|---|---|
  | L0 (page-private) | `app/<route>/_components/<Component>.tsx` | `components/<feature>/<Component>.tsx` |
  | L1 (route-group shared) | `app/(group)/_components/<Component>.tsx` | `components/<feature>/<Component>.tsx` (same physical folder as L0 — Expo has no route-group-scoped component folder; the 2nd use is a tolerated duplicate copy inside the second feature's folder) |
  | L2 (globally shared) | `components/shared/<dominio>/<Component>.tsx` | `components/shared/<dominio>/<Component>.tsx` (same as web) |

- **Never** `app/<route>/_components/` on mobile. A previous revision of this skill pointed there by mistake (copied from the Next.js convention) — that guidance is corrected here.
- **Promotion via `promote-component`**: when the same component pattern appears 3+ times (i.e., copies exist across 3+ `components/<feature>/` folders), call the dedicated skill to lift to L2 with import rewriting.
- **`components/shared/<dominio>/`** for L2: domain-based naming only (no "shared", "common", "misc").

## Sources

- Course: codewithbeto.dev/rnCourse — modules "Components and APIs" + "Style and Design" (paid, distilled).
- Knowledge skills consumed (see above).

