Stack conventions
React
React 18/19 user interfaces with function components and hooks, tested through the DOM with Testing Library.
React 18/19 user interfaces with function components and hooks, tested through the DOM with Testing Library.
Used for: react.
Principles
- Keep components and hooks pure: same props, state and context give the same JSX; side effects belong in event handlers or effects.
- Follow the Rules of Hooks: call hooks only at the top level of components and custom hooks, never in conditions, loops or callbacks.
- Derive values during render instead of mirroring props or other state into new state; keep the minimal state that represents the UI.
- Colocate state with the component that uses it; lift it only to the closest common parent, and reach for context or a store only for truly shared data.
- Use effects only to synchronise with external systems (subscriptions, non-React widgets, network on mount when no data library exists) — not to transform data or respond to events.
- Give list items stable, unique keys from the data (ids), never array indexes for lists that reorder, insert or delete.
- Treat state as immutable: create new objects/arrays on update; never mutate props or state in place.
- Memoise (
useMemo,useCallback,memo) only when a measured problem exists or the React Compiler is not handling it; correctness must not depend on memoisation. - Build accessible UI by default: semantic HTML elements first, labelled controls, keyboard support and visible focus; ARIA only to fill gaps.
- Use the repo's data-fetching layer (TanStack Query, SWR, a framework loader) for server state rather than hand-rolled fetch-in-effect code.
Conventions
Accessibility
- Use native elements (
button,a,nav,main,ul) instead of clickabledivs. - Every form control has a programmatic label; every image has meaningful
altoralt=""if decorative. - Interactive custom widgets follow the WAI-ARIA Authoring Practices keyboard patterns and manage focus (dialogs trap and restore focus).
- Do not convey information by colour alone; maintain WCAG 2.2 AA contrast.
- Announce async status changes with a live region where the user would otherwise miss them.
Concurrency
- Guard async effects against stale updates (abort with AbortController in the cleanup or ignore results after unmount).
- Use
useTransition/useDeferredValuefor expensive non-urgent updates rather than debouncing state manually.
Dependencies
- Use the repo's component library and design tokens instead of adding new UI kits or ad-hoc styles.
- Keep
reactandreact-domversions identical.
Documentation
- Document a component's props with TypeScript types and short JSDoc on non-obvious props; keep Storybook stories current where the repo uses them.
- Add a short comment where an effect, memo or ref exists for a non-obvious reason (a browser quirk, a performance fix).
Errors
- Render loading, empty and error states explicitly for every async view.
- Use error boundaries for render errors; handle event-handler and async errors where they happen.
Logging
- No
console.logleft in components; report errors through the repo's monitoring hook. - Send unexpected errors from error boundaries and failed requests to the repo's error-monitoring client with the component and action as context, never user input or tokens.
Naming
- Components in PascalCase, one exported component per file named after it; hooks start with
use. - Event handler props are
onXxx, handler implementationshandleXxx. - Boolean props read as predicates:
isOpen,hasError,disabled.
Performance
- Fix the cause (state too high, unstable props, oversized lists) before adding memoisation; profile with React DevTools Profiler.
- Virtualise long lists; lazy-load heavy routes and components with
lazy+Suspense. - Avoid creating new object/array/function props in hot paths when the child is memoised.
Security
- Never use
dangerouslySetInnerHTMLwith untrusted content; sanitise (e.g. DOMPurify) when HTML rendering is unavoidable. - Do not build
href/srcfrom user input without allowing only http(s) schemes (nojavascript:URLs). - Keep secrets out of client bundles; anything in the bundle is public.
- Treat URL parameters, API responses and storage as untrusted: validate them with a schema before use and never interpolate them into markup or
eval-like APIs (XSS). - Commit the lockfile and keep
npm auditclean of high-severity advisories for production dependencies.
Structure
- Group by feature (component, hook, styles, tests together) unless the repo uses another layout.
- Keep components small; extract subcomponents when JSX exceeds a screen or mixes concerns.
- Put pure helpers and types outside the component body so they are not recreated per render.
Testing
- Test behaviour through the DOM with Testing Library; query by role and accessible name first (
getByRole('button', { name: /save/i })), then label text, and use test ids last. - Use
user-eventfor interactions andfindBy*/waitForfor async UI instead of manual timeouts. - Do not test implementation details (internal state, hook call counts, class names).
- Mock the network at the boundary (MSW or the repo's fake API), not React internals.
- Unit-test hooks and pure helpers directly; integration-test components with their children, router and a mocked API (MSW) rather than shallow rendering.
- Name test files after the component (
InvoiceForm.test.tsx) and test titles after what the user sees or does. - Done means tested: new or changed behaviour ships with tests in the same change, and a fixed bug gets a regression test that fails without the fix.
Review checklist
- Are hooks called unconditionally at the top level, with complete dependency arrays?
- Is any state derivable from props or other state (and therefore redundant)?
- Is each useEffect synchronising with an external system rather than reacting to an event?
- Do lists use stable, data-derived keys?
- Is state updated immutably?
- Are loading, empty and error states handled for async data?
- Are interactive elements native buttons/links with accessible names?
- Do form controls have associated labels?
- Do tests query by role/label and assert user-visible behaviour?
- Is any memoisation justified by a measured or obvious cost?
- Is
dangerouslySetInnerHTMLabsent or used only with sanitised content? - Does the change reuse the repo's design system components and tokens?
Quality checks (run when your repository has the tool configured)
| Check | Kind | Tool | Blocks the pull request |
|---|---|---|---|
eslint |
lint | eslint | no |
prettier |
format | prettier | no |
tsc |
typecheck | tsc | no |
biome |
lint | biome | no |
npm-audit |
security | npm audit | no |
gitleaks |
security | gitleaks | no |