If you're using Claude Code to build React + TypeScript applications in 2026, there's one file that separates a frustrating "fix this again" loop from a genuinely productive agentic workflow: CLAUDE.md.
I've been shipping React + TypeScript apps with Claude Code for months now, and the difference between a project with a well-crafted CLAUDE.md and one without is night and day. Without it, Claude generates generic boilerplate. With it, Claude writes code that fits your architecture like it's been pair-programming with you for weeks.
Here's exactly how I set up CLAUDE.md for my React + TypeScript projects — and why every decision matters.
CLAUDE.md is a markdown file that lives in your project root (or at .claude/CLAUDE.md). Claude Code reads it at the start of every session and treats it as persistent context — your project's memory layer that survives session resets.
Think of it as a senior developer's onboarding doc, except the reader is an AI agent that will literally follow these instructions when generating code, running commands, and making architectural decisions. It's not configuration in the traditional sense — Claude reads it and tries to follow it, so specificity is everything.
There are three scopes where CLAUDE.md can live:
./CLAUDE.md): Team-shared instructions committed to source control. This is your primary file.~/.claude/CLAUDE.md): Personal preferences across all projects — your coding style, preferred tooling shortcuts.For a React + TypeScript project, the project-level file is where 90% of the value sits.
After iterating on dozens of projects, here's the structure I've landed on. Each section exists for a reason — cut any of them and you'll notice Claude's output quality drop.
Start with 2–3 lines describing what the app does, who it's for, and the core tech stack. Claude uses this to make judgment calls when you're vague.
# Project Overview
This is a B2B SaaS dashboard built with React 19, TypeScript 5.x, Vite,
and TanStack Query for server state. The backend is a .NET 9 Web API
with SQL Server. Deployed on Azure App Service.
Notice I included the backend stack too. Even if Claude is only touching frontend code, knowing the API layer is .NET helps it generate correct fetch calls, error handling patterns, and type definitions.
This is the section most developers skip — and it's the one that causes the most frustration when missing. Without it, Claude dumps components in random directories.
# Project Structure
src/
components/ # Shared UI components (atomic design: atoms, molecules, organisms)
features/ # Feature modules, each with its own components/, hooks/, types/
hooks/ # Global custom hooks
lib/ # Utility functions, API client, constants
types/ # Shared TypeScript interfaces and type definitions
pages/ # Route-level page components (maps to React Router routes)
providers/ # React context providers
styles/ # Global styles, Tailwind config extensions
The comments after each directory are critical. Claude reads them to decide where new files go. If you write "Feature modules, each with its own components/, hooks/, types/" — Claude will actually create those subdirectories when scaffolding a new feature.
This is where vague instructions kill you. "Write clean code" means nothing. Here's what actually works:
# Code Conventions
- TypeScript strict mode is ON. No `any` types unless explicitly justified with a comment.
- Use functional components with hooks. Never class components.
- Name components in PascalCase. Name hooks with `use` prefix (e.g., `useAuth`, `useDashboardData`).
- All props must have explicit TypeScript interfaces. Name them `{ComponentName}Props`.
- Use named exports, not default exports (except for lazy-loaded page components).
- 2-space indentation. Single quotes for strings. No semicolons (Prettier handles this).
- Prefer `const` over `let`. Never use `var`.
- Destructure props in function parameters: `function UserCard({ name, email }: UserCardProps)`
Every line here is a concrete, verifiable rule. Claude can check "did I use any?" — it can't check "did I write clean code?"
This is where React + TypeScript projects diverge wildly. Your CLAUDE.md needs to be explicit about which patterns you use, or Claude will default to whatever it's seen most in training data.
# State Management
- Server state: TanStack Query (React Query v5). All API calls go through custom hooks in `features/{feature}/hooks/`.
- Client state: Zustand for global UI state (theme, sidebar, modals). Keep stores in `src/stores/`.
- Form state: React Hook Form with Zod validation schemas. Schemas live next to the form component.
- URL state: Use `useSearchParams` for filters, pagination, and sort state. Don't duplicate URL state in Zustand.
# Data Fetching
- API client is in `src/lib/api.ts` using Axios with interceptors for auth tokens.
- All query keys follow the pattern: `[feature, entity, ...params]` e.g., `['dashboard', 'metrics', { period }]`.
- Mutations must invalidate related queries. Always use `queryClient.invalidateQueries()`.
- Never call APIs directly in components. Always wrap in a TanStack Query hook.
The query key convention alone saves me hours of debugging. Before I added that rule, Claude would generate random query keys and my cache invalidation was a mess.
Claude Code runs commands in your terminal. If it doesn't know your exact commands, it guesses — and guesses wrong.
# Commands
- Dev server: `pnpm dev` (Vite, port 5173)
- Build: `pnpm build` (outputs to dist/)
- Test: `pnpm test` (Vitest with React Testing Library)
- Lint: `pnpm lint` (ESLint + Prettier check)
- Type check: `pnpm typecheck` (tsc --noEmit)
- Run tests before committing. Run typecheck before pushing.
That last line is a behavioral instruction — Claude will actually run pnpm test before creating a commit if you tell it to.
This section prevents the most common code quality issues I've seen Claude produce without guidance:
# Error Handling
- All async operations must have try/catch with typed error handling.
- Use error boundaries at the route level (src/components/ErrorBoundary.tsx).
- API errors should be typed: `ApiError { status: number; message: string; code: string }`.
- Loading states must always be handled. No component should render without considering loading/error/empty states.
- Use Suspense boundaries for lazy-loaded routes.
As your project grows, cramming everything into one CLAUDE.md gets unwieldy. Anthropic recommends keeping it under 200 lines for best adherence. Here's how I scale it.
I use .claude/rules/ with path-scoped files. For example:
.claude/
CLAUDE.md # Core project info (under 200 lines)
rules/
api-hooks.md # Rules scoped to src/features/**/hooks/*.ts
components.md # Rules scoped to src/components/**/*.tsx
testing.md # Rules scoped to **/*.test.ts
Each rules file uses YAML frontmatter to specify which file paths trigger it:
---
paths:
- "src/features/**/hooks/*.ts"
---
# API Hook Rules
- Every hook must return { data, isLoading, error } at minimum.
- Use generic types: useQuery<TData, TError>.
- Stale time defaults to 5 minutes for dashboard data, 30 seconds for real-time feeds.
These rules only load when Claude touches matching files — keeping context lean and relevant.
One trick I use heavily: importing existing project files into CLAUDE.md with the @path syntax. This way, Claude always has your API schema or component library index in context without you duplicating content.
# Reference Files
See @src/types/api.ts for all API response types.
See @package.json for available scripts and dependencies.
Claude expands these at session start, so it knows your exact API types when generating components. No more guessing field names.
Here's what changed in my workflow after investing 30 minutes in a proper CLAUDE.md:
Before: I'd ask Claude to create a user profile page. It would generate a class component with inline styles, fetch data with raw useEffect + fetch, dump everything in src/components/, and use any types for the API response. I'd spend 20 minutes refactoring.
After: Same prompt. Claude generates a functional component in src/features/profile/components/, creates a useUserProfile hook using TanStack Query in the feature's hooks/ directory, types everything with interfaces from src/types/, handles loading/error/empty states, and uses Tailwind classes matching my design system. Ready for review in under 2 minutes.
That's not a marginal improvement — it's a fundamentally different development experience.
If you're starting from zero, Claude Code has a built-in command:
claude
/init
This scans your codebase and generates a starter CLAUDE.md with build commands, directory structure, and conventions it discovers. It's a solid 60% starting point — you'll want to refine the state management, data fetching, and error handling sections manually since those are where your project's personality lives.
For the interactive multi-phase flow that asks which artifacts to set up (CLAUDE.md, skills, hooks), set CLAUDE_CODE_NEW_INIT=true before running /init.
Your CLAUDE.md is the single highest-leverage file in any AI-assisted React + TypeScript project. Spend 30 minutes writing it properly and you'll save hours every week. Keep it under 200 lines, be brutally specific, use .claude/rules/ for scoped instructions, and import existing project files with @path syntax so Claude always has real context instead of guessing.
The developers who are shipping fastest with Claude Code in 2026 aren't the ones writing the best prompts — they're the ones with the best CLAUDE.md files.