---
name: "React"
description: "Always-on rules for writing and editing React components and hooks in TypeScript."
applyTo: "**/*.tsx,**/*.jsx"
---

> Web React. In React Native repositories use the React Native instructions instead — both match `.tsx` files.

# React rules

Follow the existing project conventions first; these rules apply to new and changed code.

## Components

- Use function declarations with named exports, one component per file; no `React.FC`, no default exports. Easier refactors and consistent imports.
- Type props with a `type`; destructure them with defaults in the signature and spread `...rest` onto the root element so consumers can pass `className` and `aria-*`.
- Derive values during render instead of copying props or state into new state. Synced duplicate state is the most common React bug.
- Render every state explicitly: loading, empty, error and success. Never return `null` for an error.
- Use stable ids for `key`; index keys only for static lists that never reorder. Wrong keys corrupt state.
- Name handlers `handleX` inside and props `onX`. Keeps intent readable.
- Extract a child component or custom hook once a component passes about 150 lines or 3 pieces of state.
- In Next.js, default to Server Components; add `"use client"` only for state, effects, browser APIs or event handlers.

## Hooks and effects

- Call hooks only at the top level of components and custom hooks. Rules of Hooks are not optional.
- Do not use `useEffect` to derive data, reset state or respond to events: derive in render, use a `key`, or handle it in the event handler.
- Use `useEffect` only to sync with external systems (browser APIs, widgets, timers), one concern per effect, with a cleanup function.
- Never silence `react-hooks/exhaustive-deps`; restructure the code instead. Suppressed deps cause stale closures.
- Name custom hooks after what they return (`useOrderFilters`), not how they work.
- In React 19 pass `ref` as a normal prop; do not add `forwardRef` in new code.

## Data and state

- Fetch server data with TanStack Query, route loaders or Server Components, never raw `fetch` in `useEffect`. You get caching, deduplication and no race conditions.
- Keep query keys in a factory per feature (`orderKeys.list(filters)`) so invalidation stays correct.
- Keep state as local as possible; put shareable UI state (filters, tabs, pagination) in the URL.
- Use context for rarely changing values (theme, user, locale), not for fast-changing state. Every consumer re-renders.
- Do not add `useMemo`/`useCallback` everywhere by default; memoize when profiling or referential stability requires it, or rely on the React Compiler if the project uses it.

## Accessibility and safety

- Use semantic elements: `<button>` for actions, `<a href>` for navigation. A clickable `<div>` is not keyboard accessible.
- Give every input a visible `<label>` and every icon-only button an `aria-label`.
- Never use `dangerouslySetInnerHTML` with unsanitized input; sanitize with DOMPurify if HTML is unavoidable. Prevents XSS.
- Never put secrets or API keys in client code or `NEXT_PUBLIC_`/`VITE_` variables. Everything in the bundle is public.
- Validate `href` values from user data; block `javascript:` URLs.

## Types and tests

- Keep TypeScript `strict`; no `any` and no non-null `!` to silence errors. Use `unknown` and narrow.
- Type event handlers with React's event types (`React.ChangeEvent<HTMLInputElement>`).
- Test behaviour with React Testing Library, querying by role and label, not by class names or implementation details.
- Mock the network with MSW rather than mocking hooks. Tests stay valid through refactors.

Go deeper: for larger tasks use the react-development, react-architecture, react-testing and react-performance skills, and frontend-code-review before opening a pull request.
