React Hooks Best Practices
Disciplined hook usage: deps arrays, custom hooks, refs, and effect minimalism.
# CLAUDE.md — React Hooks Best Practices
## Rules of Hooks
- Call hooks at the top level of a function component or another hook. Never inside loops, conditions, or after early returns.
- Hook names start with `use`. ESLint enforces this and it's load-bearing for the linter — don't rename around it.
## useState
- Initialize with the value, not a getter, unless construction is expensive: `useState(() => expensiveInit())`.
- One state per concern. Don't combine unrelated state into one big object.
- For derived state, compute during render. Don't sync via `useEffect`.
## useEffect — the last resort
- Effects are for synchronizing with external systems (DOM, network, timers). Not for "running code when X changes".
- If the body of an effect could be an event handler, it should be.
- Always specify the dep array. Empty `[]` only when you truly mean "once" and you're not using any value from the render.
- Always return a cleanup function for subscriptions, intervals, observers.
- Two effects, two responsibilities — never combine unrelated work into one effect.
## useRef
- For values that need to persist across renders without causing re-renders.
- For DOM refs: `useRef<HTMLInputElement>(null)`. Type the element narrowly.
- Mutating `ref.current` doesn't re-render. If you need a re-render, use state.
## useMemo / useCallback
- Default to *not* using them. They have a real cost — memoization itself takes work.
- Use them when:
- A value is passed to a memoized child and identity matters
- A computation is provably expensive (you've measured)
- Don't memoize primitive values. Don't memoize unstable inputs.
## Custom hooks
- A hook is a function that calls other hooks. Anything that doesn't is a utility — keep it as a plain function.
- One hook, one purpose. Compose smaller hooks; don't write a 200-line super-hook.
- Return tuples for two values where order is obvious (`[value, setValue]`). Return objects when you have 3+ named values.
## useReducer
- Reach for `useReducer` when state transitions are complex or multiple events update overlapping state.
- Reducers must be pure. No fetches, no side effects.
- Action types are string literals — type them as a discriminated union.
## Don't
- Don't use `useEffect` for data fetching when a server framework or query library is available.
- Don't read state values inside an effect via stale closures — add them to deps or use a ref.
- Don't create new objects/arrays in render and pass them to memoized children. Memoize them.
- Don't disable `react-hooks/exhaustive-deps`. Fix the dependency, don't hide the warning.
Other TypeScript templates
Next.js App Router + TypeScript Rules
Server Components, Server Actions, and TypeScript discipline for the Next.js App Router.
Next.js + Server Actions + Shadcn UI
Forms with Server Actions, Zod validation, and Shadcn UI primitives.
Next.js + Supabase Auth
Auth flows with Supabase SSR, cookies, and Row Level Security policies.
Next.js + Tailwind + Prisma Stack
Full-stack Next.js with Prisma ORM, Tailwind CSS, and Postgres.
Next.js SEO + Metadata Rules
Metadata API, sitemap, robots, OG images, and structured data done right.