agentleFS
motola10motola10/claude-code-base/.claude/skills/react-standards/SKILL.md

react-standards

Skill1 starsChanged 5 months agoMITView on GitHub

Best-practice coding standards for React UI code (manifest: package.json with react). Referenced by the review agents. Covers component design, hooks, state management, rendering/performance, error boundaries, client logging, forms, testing, accessibility. Pairs with typescript-standards; Next.js apps ALSO load nextjs-standards. Do NOT use for backend or non-React frontend code.

2 other projects use a version of this.

Adopted by

Most recently taken up by

The file

Show its rules and text

We split this skill into the rules it gives an agent

27 rules. About 0 are common, 10 are distinctive, and 17 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈10

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 17

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Target: all React source — components, hooks, context, pages.

    line 49
  2. Very specific:

    Extended by nextjs.md: when Next.js is the host — Next.js rules override or extend these where they conflict.

    line 51
  3. Very specific:

    REACT-COMP-1 (MUST): Write function components.

    line 59
  4. Distinctive:

    REACT-COMP-3 (SHOULD): Keep components small and focused — a single visual or behavioral concern.

    line 67
  5. Distinctive:

    REACT-COMP-4 (SHOULD): Prefer named exports for components.

    line 70
  6. Distinctive:

    REACT-HOOK-1 (MUST): Follow the Rules of Hooks exactly:

    line 96
  7. Distinctive:

    Call hooks only at the top level of a component or custom hook — never inside loops, conditions, or nested functions.

    line 97
  8. Distinctive:

    Call hooks only from React function components or custom hooks.

    line 99
  9. Very specific:

    REACT-HOOK-4 (SHOULD): Keep useEffect dependency arrays accurate and complete.

    line 104
  10. Very specific:

    REACT-HOOK-5 (SHOULD NOT): Use useEffect to synchronize unrelated concerns in one effect block.

    line 107
  11. Very specific:

    REACT-STATE-1 (MUST): MUST NOT mutate state directly.

    line 130
  12. Very specific:

    REACT-STATE-2 (SHOULD): Use useState for simple, isolated local state.

    line 132
  13. Distinctive:

    REACT-STATE-3 (SHOULD): Use React Context for shared state that is stable and infrequently updated (themes, locale, current user).

    line 135
  14. Distinctive:

    REACT-STATE-5 (SHOULD): Keep state at the lowest common ancestor that needs it, then lift only when multiple siblings require it.

    line 150
  15. Very specific:

    REACT-PERF-1 (MUST): Every list item rendered with .map() MUST have a stable, unique key prop — not an array index unless the list is …

    line 158
  16. Very specific:

    REACT-PERF-3 (SHOULD): Use React.lazy + Suspense for code-splitting large components or routes.

    line 163
  17. Very specific:

    REACT-ERR-1 (MUST): Every page or major feature subtree MUST be wrapped in an Error Boundary to prevent an uncaught render error from …

    line 185
  18. Distinctive:

    Do not render nothing or re-throw silently.

    line 189
  19. Very specific:

    REACT-SEC-1 (MUST NOT): Use dangerouslySetInnerHTML with untrusted, user-supplied content.

    line 198
  20. Very specific:

    If you must inject HTML (e.g.

    line 199
  21. Distinctive:

    REACT-TEST-1 (MUST): Test with React Testing Library (RTL) — test components through their rendered DOM and user interactions, not internal …

    line 209
  22. Very specific:

    REACT-TEST-3 (SHOULD): Prefer userEvent over fireEvent for simulating user interactions — it triggers the full browser event sequence …

    line 215
  23. Very specific:

    REACT-TEST-4 (SHOULD): Use screen queries (screen.getByRole, screen.getByLabelText) over container-based queries.

    line 218
  24. Very specific:

    REACT-A11Y-1 (SHOULD): Use semantic HTML elements (<button>, <nav>, <main>, <section>) rather than generic <div>/<span> with ARIA roles …

    line 242
  25. Very specific:

    REACT-A11Y-2 (SHOULD): Every interactive element MUST be reachable and activatable via keyboard.

    line 244
  26. Very specific:

    REACT-A11Y-3 (SHOULD): All images MUST have an alt prop.

    line 247
  27. Very specific:

    REACT-LOG-1 (MUST NOT): Leave debugging console.log/console.debug calls in committed or production client code (frontend mirror of …

    line 257

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

React.js Coding Standard (v1.0)

Status: Locked v1.0. Covers React as a UI library, not a full framework. This standard always pairs with typescript.md (language rules) and is extended by nextjs.md when Next.js is the host framework. Those documents own their respective concerns; this document owns component design, hooks usage, state management, rendering, forms, and testing patterns.

On adoption this becomes .claude/skills/reactjs-standards/SKILL.md + references/*.md.

Toolchain verified current as of 2026-06-13 (via Context7 + npm): React 19.2.x, @testing-library/react 16.x, Vitest 4.x, @vitejs/plugin-react 6.x, Zustand 5.x, TanStack Query 5.x, React Hook Form 7.x + @hookform/resolvers 5.x, Zod 4.x. Rules are written version-agnostically; these are reference points, not pins.


How to read this document

Constraint language (RFC 2119)

  • MUST / MUST NOT — hard requirement; a violation is a blocking issue.
  • SHOULD / SHOULD NOT — strong recommendation; deviate only with a written // Justification:.
  • MAY — optional.

Rule IDs

Stable REACT-AREA-NUMBER (e.g. REACT-COMP-3). The reviewer MUST cite the rule ID. Never renumber.

Severity mapping

This document says…Reviewer severityGate
MUST/MUST NOT affecting correctness / securityCriticalBlocks merge
MUST/MUST NOT elsewhereCritical / Warning (by impact)Blocks if it risks correctness
SHOULD without justificationWarningShould fix
MAY / stylisticSuggestionNice to have

Scope & Assumptions

  1. Target: all React source — components, hooks, context, pages.
  2. Pairs with typescript.md — all TypeScript rules apply; on overlap, the stricter rule wins.
  3. Extended by nextjs.md when Next.js is the host — Next.js rules override or extend these where they conflict.
  4. Function components only — class components are permitted only to implement Error Boundaries (no class-component API exists for them yet).
  5. Custom overrides: the project's custom-standards skill overrides this document on conflict.

1. Component Design

  • REACT-COMP-1 (MUST): Write function components. Class components MUST NOT be used in new code except as Error Boundaries (where static getDerivedStateFromError + componentDidCatch still require a class).
  • REACT-COMP-2 (MUST): Each component MUST have an explicit TypeScript props type — an interface or inline object type. Avoid React.FC<Props>: it adds no meaningful value over a plain typed function and forces an opaque ReactElement | null return type instead of the inferred one. Annotate the return type (JSX.Element, React.ReactNode) only when the inferred type is not obvious to a reader.
  • REACT-COMP-3 (SHOULD): Keep components small and focused — a single visual or behavioral concern. Extract sub-components or custom hooks when a component grows beyond ~100 lines or has mixed unrelated concerns.
  • REACT-COMP-4 (SHOULD): Prefer named exports for components. Default exports make refactoring and consistent import names harder across a large codebase.
  • REACT-COMP-5 (SHOULD): Co-locate a component's types, helpers, and styles in the same directory. Barrel index.ts files are fine for public API surfaces; avoid deep re-export chains.
// ✅ REACT-COMP-2: explicit props interface, no React.FC
interface ButtonProps {
  label: string;
  onClick: () => void;
  disabled?: boolean;
}

export function Button({ label, onClick, disabled = false }: ButtonProps) {
  return (
    <button onClick={onClick} disabled={disabled}>
      {label}
    </button>
  );
}

2. Hooks

  • REACT-HOOK-1 (MUST): Follow the Rules of Hooks exactly:
    • Call hooks only at the top level of a component or custom hook — never inside loops, conditions, or nested functions.
    • Call hooks only from React function components or custom hooks.
  • REACT-HOOK-2 (MUST): Name custom hooks with the use prefix (e.g. useAuth, useCart) so linters can enforce the Rules of Hooks on them.
  • REACT-HOOK-3 (SHOULD): Declare useEffect cleanup functions when the effect subscribes to anything external (timers, event listeners, subscriptions). A missing cleanup is a memory leak.
  • REACT-HOOK-4 (SHOULD): Keep useEffect dependency arrays accurate and complete. Enable the react-hooks/exhaustive-deps ESLint rule. Suppressing it with // eslint-disable requires a written justification — it usually signals a design smell.
  • REACT-HOOK-5 (SHOULD NOT): Use useEffect to synchronize unrelated concerns in one effect block. Separate effects by concern; each effect should do one thing.
  • REACT-HOOK-6 (SHOULD NOT): Reach for useMemo/useCallback prematurely. Add them only when a profiler confirms a concrete rendering bottleneck — unnecessary memoization adds overhead and obscures code.
// ✅ REACT-HOOK-3: cleanup prevents memory leak
function useWindowWidth(): number {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    const handler = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler); // cleanup
  }, []);
  return width;
}

3. State Management

  • REACT-STATE-1 (MUST): MUST NOT mutate state directly. Always produce a new value — spread objects, map/filter arrays; never push/splice in place.
  • REACT-STATE-2 (SHOULD): Use useState for simple, isolated local state. Prefer useReducer when state transitions are complex, have multiple sub-fields, or involve "previous state" logic — reducers are easier to test and reason about.
  • REACT-STATE-3 (SHOULD): Use React Context for shared state that is stable and infrequently updated (themes, locale, current user). SHOULD NOT use Context as a general-purpose store for high-frequency state updates — it re-renders every consumer on every change.
  • REACT-STATE-4 (SHOULD): For non-trivial client-side global state (carts, auth, UI panels), prefer Zustand (minimal API, no provider wrapping, no boilerplate) over a hand-rolled context-based store. Jotai is an acceptable alternative where an atomic model fits better. Redux Toolkit is reserved for large/legacy apps that already use it — don't reach for it in new projects. Don't introduce a state library at all for small apps that useState/useReducer + Context already serve.
  • REACT-STATE-6 (SHOULD): Manage server/async state (data fetched from an API) with TanStack Query — it owns fetching, caching, deduping, and refetching. You MUST NOT re-implement this with useEffect + fetch + useState, which leaks loading/error handling and cache logic into every component.
    • Next.js override: under the App Router, prefer Server Components + fetch for server-rendered data (see nextjs.md); TanStack Query applies to client-fetched data only.
  • REACT-STATE-5 (SHOULD): Keep state at the lowest common ancestor that needs it, then lift only when multiple siblings require it. Avoid prop-drilling more than two levels — refactor to context or a state library at that point.

4. Rendering & Performance

  • REACT-PERF-1 (MUST): Every list item rendered with .map() MUST have a stable, unique key prop — not an array index unless the list is purely static and will never reorder or change.
  • REACT-PERF-2 (SHOULD): Wrap expensive subtrees in React.memo only when profiling shows a measurable regression. Memoize prop references passed to those components with useMemo/ useCallback so the memo comparison works.
  • REACT-PERF-3 (SHOULD): Use React.lazy + Suspense for code-splitting large components or routes. Wrap each lazy boundary in <Suspense fallback={…}>.
  • REACT-PERF-4 (SHOULD): Wrap user-initiated state updates that trigger expensive re-renders in startTransition to mark them as non-urgent, keeping the UI responsive.
// ✅ REACT-PERF-3
const HeavyChart = React.lazy(() => import('./HeavyChart'));

function Dashboard() {
  return (
    <Suspense fallback={<Spinner />}>
      <HeavyChart />
    </Suspense>
  );
}

5. Error Handling

  • REACT-ERR-1 (MUST): Every page or major feature subtree MUST be wrapped in an Error Boundary to prevent an uncaught render error from crashing the entire app. The boundary MUST log the error (e.g. to an error-tracking service) in componentDidCatch.
  • REACT-ERR-2 (SHOULD): Display a user-friendly fallback UI in the Error Boundary render method. Do not render nothing or re-throw silently.
  • REACT-ERR-3 (SHOULD): For async data fetching errors (e.g. failed API calls), handle them in TanStack Query's error state or the component's error branch — do not let them surface as unhandled promise rejections.

6. Security

  • REACT-SEC-1 (MUST NOT): Use dangerouslySetInnerHTML with untrusted, user-supplied content. If you must inject HTML (e.g. from a CMS), sanitize it first with a library such as DOMPurify.
  • REACT-SEC-2 (MUST NOT): Construct href/src props from untrusted input without validation. javascript: URIs in href are an XSS vector.
  • REACT-SEC-3 (SHOULD NOT): Store sensitive data (tokens, PII) in component state or Context where it can be inspected via React DevTools. Use httpOnly cookies or a secure token store.

7. Testing

  • REACT-TEST-1 (MUST): Test with React Testing Library (RTL) — test components through their rendered DOM and user interactions, not internal implementation details (state, refs, component instances). Use Vitest as the test runner in Vite projects; Jest is acceptable in CRA/webpack setups.
  • REACT-TEST-2 (MUST): Maintain ≥ 80% coverage on changed files (same policy as all standards in this suite). Flag tests with no assertions or that only assert truthiness as inadequate.
  • REACT-TEST-3 (SHOULD): Prefer userEvent over fireEvent for simulating user interactions — it triggers the full browser event sequence (mousedown → mouseup → click, etc.) and is more realistic.
  • REACT-TEST-4 (SHOULD): Use screen queries (screen.getByRole, screen.getByLabelText) over container-based queries. Role-based queries are closest to how users and assistive technology perceive the UI.
  • REACT-TEST-5 (SHOULD): Mock only at system boundaries (HTTP, timers, browser APIs). Do not mock child components or internal hooks — that tests the mock, not the component.
// ✅ REACT-TEST-3 + REACT-TEST-4
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';

test('calls onClick when clicked', async () => {
  const onClick = vi.fn();
  render(<Button label="Save" onClick={onClick} />);
  await userEvent.click(screen.getByRole('button', { name: 'Save' }));
  expect(onClick).toHaveBeenCalledOnce();
});

8. Accessibility

  • REACT-A11Y-1 (SHOULD): Use semantic HTML elements (<button>, <nav>, <main>, <section>) rather than generic <div>/<span> with ARIA roles bolted on.
  • REACT-A11Y-2 (SHOULD): Every interactive element MUST be reachable and activatable via keyboard. Do not attach click handlers to <div> or <span> without an onKeyDown handler and tabIndex.
  • REACT-A11Y-3 (SHOULD): All images MUST have an alt prop. Decorative images use alt="". Enable the jsx-a11y ESLint plugin to catch violations at lint time.

9. Logging & Observability

Server-side/structured logging is owned by the runtime standard (nodejs.md §8, or nextjs.md for the server tier). This section owns the client/browser slice, which no other standard covers.

  • REACT-LOG-1 (MUST NOT): Leave debugging console.log/console.debug calls in committed or production client code (frontend mirror of NODE-STYLE-7). Intentional console.error/ console.warn for genuine error/warning paths is allowed; prefer routing them through a thin logger wrapper so output can be silenced or forwarded centrally. Enable the no-console ESLint rule (allowing warn/error) to enforce this.
  • REACT-LOG-2 (SHOULD): Route client-side errors and unhandled rejections to an observability / error-tracking service (e.g. Sentry) rather than only console. This generalizes REACT-ERR-1: the Error Boundary reports render errors, and a global handler captures the rest.
  • REACT-LOG-3 (MUST NOT): Log secrets, tokens, or PII to the browser console or client logs — they are fully inspectable via DevTools and may be captured by third-party error trackers. This is the logging counterpart to REACT-SEC-3; scrub/deny-list sensitive fields before any client log or error report leaves the app.

10. Forms

  • REACT-FORM-1 (SHOULD): For any non-trivial form (multiple fields, validation, or submission state), use React Hook Form (useForm) rather than hand-wiring useState per field. It keeps inputs uncontrolled by default, which minimizes re-renders.
  • REACT-FORM-2 (SHOULD): Validate with a Zod schema wired through @hookform/resolvers/zod's zodResolver. Derive the form's TypeScript type from the schema with z.infer<typeof schema> — one source of truth for shape, validation, and types (this is the same Zod v4 choice locked in typescript.md TS-TYPE-2).
  • REACT-FORM-3 (SHOULD): Drive submission through handleSubmit and surface field errors from RHF's formState.errors — do not roll a parallel error-tracking state alongside it.
// ✅ REACT-FORM-1/2/3: RHF + Zod, single source of truth
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';

const schema = z.object({ name: z.string().min(1), age: z.number().int().positive() });
type FormValues = z.infer<typeof schema>;

function SignupForm() {
  const { register, handleSubmit, formState: { errors } } =
    useForm<FormValues>({ resolver: zodResolver(schema) });

  return (
    <form onSubmit={handleSubmit((data) => submitSignup(data))}>
      <input {...register('name')} />
      {errors.name && <span role="alert">{errors.name.message}</span>}
      <input type="number" {...register('age', { valueAsNumber: true })} />
      <button type="submit">Sign up</button>
    </form>
  );
}

11. Styling

  • REACT-STYLE-1 (SHOULD): Pick one styling approach (Tailwind CSS, CSS Modules, or a CSS-in-JS library) and apply it consistently across the codebase. Mixing approaches without a documented reason fragments the styling story and bloats the bundle. The concrete choice is delegated to the project's custom-standards overlay — this standard does not mandate one.

Resolved Decisions (v1.0)

#QuestionDecisionRule
1State library defaultZustand SHOULD for global client state; Jotai acceptable; Redux Toolkit only for large/legacy; no library for trivial stateREACT-STATE-4
2Async/server stateTanStack Query SHOULD; bans useEffect+fetch cache patterns; Next.js App Router prefers Server ComponentsREACT-STATE-6
3StylingNot mandated — one meta-rule (pick one, be consistent); concrete choice deferred to custom-standardsREACT-STYLE-1
4FormsReact Hook Form + Zod (zodResolver, z.infer) — reinforces TS-TYPE-2REACT-FORM-1/2/3

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.