Most Claude Code setups for React Native stop at a CLAUDE.md file. That's the foundation: it tells the agent your conventions. But conventions alone don't stop the two failures I see most often in agent-built Expo features:
- The agent starts writing before it understands the codebase, and you get a feature that works but ignores how the rest of the app is built.
- The agent says "done" when it isn't: types don't check, a Supabase table has no RLS, and the screen has never rendered an error state.
This post is the workflow I use to fix both: four stages, each backed by a Claude Code feature, with the files to set it up. It assumes you already have a CLAUDE.md with your commands and conventions.
The four stages
| Stage | Claude Code feature | What it prevents |
|---|---|---|
| Plan | Plan mode | Code written before the codebase is understood |
| Build | A project skill (/new-feature) |
Inconsistent structure across features |
| Review | Read-only subagents | Security and convention mistakes the builder can't see |
| Verify | A verifier subagent + a Stop hook | "Done" that isn't done |
Stage 1: Plan before any edit
Start every non-trivial feature in plan mode. Press Shift+Tab to cycle permission modes until plan mode is active, or type /plan. In plan mode Claude explores and proposes but can't edit files. When it needs to research the codebase, it delegates to a built-in read-only Plan subagent, so the exploration output stays out of your main conversation.
A good planning prompt for an Expo + Supabase feature:
Plan a "favorites" feature: users can heart a workout and see a Favorites tab.
Before planning, find:
- how existing features are structured under features/
- how data fetching is done (queries/ hooks)
- the most recent migration in supabase/migrations/ and its RLS pattern
- how tabs are defined in app/(tabs)/_layout.tsx
The plan must list: the migration (with RLS and policies), the query hooks,
the route files, the components, and the tests. Don't write code yet.
Then read the plan. This is the cheapest moment to catch a wrong assumption: a plan that puts supabase.from() calls inside a screen component costs ten seconds to correct now and an hour to refactor later. When the plan is right, approve it and let Claude build.
Stage 2: Build with a skill, not a paragraph
If you build features the same way every time, stop retyping the instructions. A skill is a folder with a SKILL.md that you invoke as a slash command. Put project skills in .claude/skills/ and commit them so the whole team gets the same workflow.
<!-- .claude/skills/new-feature/SKILL.md -->
---
name: new-feature
description: Scaffold a new feature in this Expo + Supabase app following project conventions.
disable-model-invocation: true
---
Build the feature described by the user, following this order:
1. **Migration** in `supabase/migrations/<timestamp>_<name>.sql`:
- `create table` and `enable row level security` in the same file
- policies scoped to `(select auth.uid())`; never `using (true)` on user data
- an allow test and a deny test in `supabase/tests/`
2. **Query hooks** in `queries/<feature>.ts` using TanStack Query.
No data fetching inside components.
3. **Feature folder** `features/<feature>/` for screens and components.
4. **Routes** in `app/` that only import and render the feature screen.
5. Every screen that fetches data has loading, empty, and error states.
Use theme tokens from `theme/tokens.ts`. Navigation comes from `expo-router` only.
When finished, run `npm run typecheck` and the tests, and report the results.
Now building a feature is:
/new-feature favorites: heart a workout, Favorites tab lists hearted workouts
disable-model-invocation: true means the skill only runs when you call it, which is what you want for a workflow that creates files.
Stage 3: Review with read-only subagents
The agent that wrote the code is the worst reviewer of it: it has the same blind spots it had while writing. A subagent runs in its own context window with its own system prompt and tool restrictions, which makes it a fresh pair of eyes that can't accidentally "fix" things during review.
Subagents are Markdown files with YAML frontmatter in .claude/agents/. Two are worth having in every Expo + Supabase repo.
An RLS reviewer
<!-- .claude/agents/rls-reviewer.md -->
---
name: rls-reviewer
description: Reviews Supabase migrations and policies for RLS mistakes. Use proactively after any change in supabase/.
tools: Read, Grep, Glob
model: sonnet
---
You review Supabase SQL for security problems. You never edit files.
Check every migration changed on this branch for:
- tables created without `enable row level security` in the same file
- `using (true)` or `with check (true)` on tables holding user data
- update policies that let users change privileged columns (role, plan, is_admin, credits)
- views without `security_invoker = true`
- `security definer` functions without an `auth.uid()` check
- storage policies that don't scope paths to the user's folder
- missing deny tests in supabase/tests/
Report findings as Critical / Warning / OK, with file and line for each.
Because tools lists only Read, Grep, Glob, this subagent can't edit or run anything. It reads and reports.
A mobile conventions reviewer
<!-- .claude/agents/rn-reviewer.md -->
---
name: rn-reviewer
description: Reviews React Native changes for project conventions and mobile pitfalls. Use proactively after UI changes.
tools: Read, Grep, Glob, Bash
model: sonnet
---
You review React Native code. You never edit files.
Run `git diff main...HEAD` and check the changed files for:
- data fetching inside useEffect or components instead of queries/ hooks
- secrets or keys in EXPO_PUBLIC_ variables
- imports from @react-navigation/* instead of expo-router
- lists rendered with .map in ScrollView instead of FlatList/FlashList
- icon-only buttons without accessibilityLabel
- forms without keyboard handling
- hardcoded colors or spacing instead of theme tokens
- missing loading, empty, or error states
Report Critical / Warning / Suggestion with file and line.
After the build stage, ask for both reviews:
Use the rls-reviewer and rn-reviewer subagents to review this branch in parallel,
then fix every Critical finding.
The reviews run in their own contexts, only their summaries come back, and the main conversation does the fixing with full context of what it built.
Stage 4: Verify, and make "done" mean done
Two layers here: one the agent uses on purpose, one that runs whether it remembers or not.
A verifier subagent for noisy output
Test and typecheck output is long, and you rarely need all of it in your main context. A subagent that runs the checks and returns only failures keeps the main conversation clean:
<!-- .claude/agents/verifier.md -->
---
name: verifier
description: Runs typecheck, lint, and tests, and reports only failures. Use before reporting any task as complete.
tools: Bash, Read
model: haiku
---
Run, in order:
1. npm run typecheck
2. npx expo lint
3. npx vitest run
4. npx supabase test db (if supabase/ changed)
Report only failures: the command, the file, the line, and the error.
If everything passes, say so in one line.
Routing a mechanical task like this to a smaller, cheaper model is one of the cost controls subagents are designed for.
A Stop hook as the backstop
Instructions can be forgotten; hooks can't. Add a Stop hook in .claude/settings.json that typechecks whenever Claude finishes a turn:
{
"hooks": {
"Stop": [
{
"hooks": [
{ "type": "command", "command": "npx tsc --noEmit" }
]
}
]
}
}
If types fail, you see it immediately, before you read "Done!" and move on.
What a full run looks like
-
Shift+Tabinto plan mode. Describe the feature and what to research. Review and approve the plan. -
/new-feature favorites: …to build it in your conventions. - "Use rls-reviewer and rn-reviewer in parallel, then fix every Critical."
- "Use the verifier." Fix anything it reports.
- Read the diff yourself before committing.
Step 5 isn't optional. The workflow makes agent output reliably reviewable, not reliably correct. You're still the last reviewer.
Three rules that keep this working
Keep subagent descriptions short and specific. Claude uses the description field to decide when to delegate, and all descriptions sit in context. "Reviews Supabase migrations for RLS mistakes. Use proactively after any change in supabase/." routes well. A paragraph doesn't.
Read-only reviewers should stay read-only. Don't give review subagents Edit or Write. A reviewer that fixes things isn't reviewing.
Commit .claude/. Skills, subagents, and settings in the repo mean every teammate (and every CI run) gets the same workflow. Personal tweaks go in your user-level ~/.claude/ folder.
The part no workflow fixes
All four stages work better when the codebase itself is consistent. An agent copies the patterns it finds: if three features fetch data in useEffect, the fourth will too, whatever your skill says. Reviewers catch some of that drift; a clean starting structure prevents it.
That's the idea behind AppLighter: Expo + Supabase templates where the conventions this workflow enforces (query hooks, RLS in every migration, theme tokens, thin routes) are already in place, so Claude Code extends a codebase that's consistent from day one.
What's in your .claude/agents/ folder? I'm always looking for the next reviewer worth adding.
Claude Code features change quickly. The file formats here follow the current Claude Code docs; check them if something doesn't load.
Top comments (0)