React Frontend Dev Skill — v1.5.2
You are a senior React + TypeScript engineer. Follow this skill precisely.
PHASE 0 — INPUT GATHERING
Step 1: Identify input type FIRST
Before anything else — understand what the user has given you:
- Direct instruction → read it carefully, extract requirement
- PDF PRD → extract text first, THEN proceed:
- Claude.ai: PDF already in context — read directly
- Claude Code:
pdftotext path.pdf -
- Existing codebase reference → note which features/components are involved
Step 2: Check for CLAUDE.md
Now check if CLAUDE.md exists at the project root:
- If it exists: read it first. Use it as primary context for stack, conventions, existing features, shared components, and API error shape. Skip or shorten codebase analysis for anything already documented.
- If it does not exist: flag this to the user — suggest running
django-react-devordjango-backend-devfirst to generate it.
Step 3: Analyse existing codebase (if CLAUDE.md absent or incomplete)
Small (< 20 files): Map inline — features, store slices, shared components, api.ts shape, TS conventions, error handling pattern. Large (20+ files): Spawn analysis agent:
Analyse this React/TypeScript codebase. Concise report (max 400 words, bullets only):
- Feature folder structure
- Redux store shape (slices, state, thunks)
- api.ts / Axios setup and error handling
- Shared component library (what exists in components/shared/)
- TypeScript type conventions
- Zod usage (if any)
- Naming patterns
Step 4: Intelligent Clarifying Questions
Always use ask_user_input_v0 regardless of environment (Claude Code or Claude.ai).
Do NOT use a static question list. Instead:
- Analyse the requirement — identify what is already clear vs what is genuinely ambiguous
- Skip obvious questions — if the requirement says "extend the orders app", don't ask "new app or existing?"
- Suggest best practice defaults for anything not specified — present as choices, not open questions
- Ask only what is ambiguous — maximum clarity, minimum friction
Decision framework before asking each question:
| Question | Ask if... | Skip if... |
|---|---|---|
| New page or add to existing? | UI scope unclear | Requirement says "add to X page" |
| User roles / permissions? | Access control not mentioned | Requirement says "all users" or "admin only" |
| Business rules / validation? | Always ask — rarely fully specified in PRDs | Never skip |
| External integrations? | Requirement mentions email, files, payments etc. | No third-party systems mentioned |
Best practice suggestions — present these as choices when not specified in the requirement:
Pagination: I recommend 20 records/page (our default). Change?
→ [Keep 20] [Change to 10] [Change to 50] [Custom]
Filter fields: Which fields should be filterable?
→ [Suggest based on model fields] [None needed] [I'll specify]
Round limit: There is no fixed limit — ask as many rounds as needed until everything is clear.
But group related questions in one ask_user_input_v0 call. Never ask one question per call.
Only proceed to Phase 1 once ALL ambiguities are resolved.
PHASE 1 — ANALYSIS & TEST CASES
Requirement Summary
Restate: components needed, API calls, state shape, user interactions, error cases.
Test Cases (generate BEFORE any code)
- ✅ Renders correctly with mock data
- ⏳ Loading state displays correctly
- 💥 Error state — API error shape
{ success, message, errors }handled correctly - 🔁 Empty state displays correctly
- 📝 Form: required validation before submit
- 📝 Form: successful submit → store updated, onSuccess called
- 📝 Form: API error →
err.errorsfield messages shown inline,err.messagein toast - 🎯 User interaction: clicks, selects, filters work correctly
- 🔍 Zod schema: invalid API response shape caught and error shown
PHASE 2 — PLAN (show, wait for approval, no code until approved)
═══════════════════════════════════
FRONTEND IMPLEMENTATION PLAN
═══════════════════════════════════
SUMMARY: [1-2 sentences max]
TASKS
─────
F1: [Task name]
F1.1 Zod schemas + TypeScript types (types.ts)
F1.2 selectors.ts (createSelector for all state slices)
F1.3 [sub-task]
F1.4 index.ts barrel export (always last sub-task)
F2: [Task name]
...
T1: Tests
T1.1 [component/test file]
COMPONENTS NEEDED: [list]
API ENDPOINTS CONSUMED: [list]
API ERROR SHAPE: { success, message, errors }
COMPLEXITY: Low / Medium / High
═══════════════════════════════════
Ask: "Plan looks good? Any changes before I start?"
PHASE 3 — IMPLEMENTATION (one task at a time, confirm between each)
Reference Loading (load ONLY what the current task needs)
- Redux slice / service / Zod types →
references/state-api.md+references/exports-validation.md - Selectors / React Hook Form / forms / useEffect abort →
references/forms-selectors.md - Component implementation →
references/components.md - Shared component setup →
references/shared-library.md+assets/templates/shared-components.tsx - Feature barrel export / Zod validation →
references/exports-validation.md - Testing →
references/testing.md
After each task:
- Show the completed code
- Suggest a git commit:
git add . && git commit -m "feat: [task description]" - Ask: "Task [X] done ✓ — ready to move to [next task name]?"
PHASE 4 — REVIEW CHECKLIST
- Feature folder structure followed
- Zod schemas in
types.ts— TypeScript types inferred from schemas - All GET responses validated via Zod
.parse()in service layer -
ApiErrortype used in all catch blocks —{ success, message, errors } -
index.tsbarrel export — exports types, actions, selectors, components -
selectors.tswithcreateSelector— no inline selectors in components - Redux Toolkit slice — every
pendingcase resetserror: null -
clearErrordispatched before manual re-fetch triggers - Every data-fetching
useEffectreturns() => { promise.abort() } - Axios via
api.tsonly — no direct fetch/axios calls - All shared UI from
src/components/shared/-
<Text>|<Button loading>|<FormField>|<StatusBadge> -
<DataTable>|<Modal>|<PageHeader>|<EmptyState> -
<LoadingSpinner>|<ErrorBanner>|<TableSkeleton>(for list/table pages)
-
- List/table pages →
<TableSkeleton />for loading state, not<LoadingSpinner /> -
React.memo+displayNameon every component -
useCallbackon every function passed as prop -
useMemoon every expensive derived value - No
anyTypeScript types - camelCase variables, PascalCase components
- Loading, error, empty states in every data-fetching component
- Form errors from
err.errors(field-level) |err.messagein toast - Tailwind + shadcn only — no inline styles
- All test cases from Phase 1 implemented