Skip to content

Latest commit

 

History

History
101 lines (78 loc) · 5.31 KB

File metadata and controls

101 lines (78 loc) · 5.31 KB

General Rules

  • MUST: Use @antfu/ni. Use ni to install, nr SCRIPT_NAME to run. nun to uninstall.
  • MUST: Use TypeScript interfaces over types.
  • MUST: Keep all types in the global scope.
  • MUST: Use arrow functions over function declarations
  • MUST: Never comment unless absolutely necessary.
    • If the code is a hack (like a setTimeout or potentially confusing code), it must be prefixed with // HACK: reason for hack
    • Do not delete descriptive comments >3 lines without confirming with the user
  • MUST: Use kebab-case for files
  • MUST: Use descriptive names for variables (avoid shorthands, or 1-2 character names).
    • Example: for .map(), you can use innerX instead of x
    • Example: instead of moved use didPositionChange
  • MUST: Frequently re-evaluate and refactor variable names to be more accurate and descriptive.
  • MUST: Do not type cast ("as") unless absolutely necessary
  • MUST: Remove unused code and don't repeat yourself.
  • MUST: Always search the codebase, think of many solutions, then implement the most elegant solution.
  • MUST: Put all magic numbers in constants.ts using SCREAMING_SNAKE_CASE with unit suffixes (_MS, _PX).
  • MUST: Put small, focused utility functions in utils/ with one utility per file.
  • MUST: Use Boolean over !!.

React Rules

  • MUST: This project uses React Compiler — never manually use useMemo, useCallback, React.memo, or forwardRef. The compiler handles memoization automatically.

Mental Model

  • MUST: Treat components as render functions that re-execute on every state change.
  • MUST: Keep components pure — same props, same output. Side effects belong in useEffect or event handlers.
  • MUST: Understand the render cycle: state change → re-render → commit to DOM → effects run.

State & Reactivity

  • MUST: Use useState for local component state.
  • MUST: Use functional updates when new state depends on old: setCount((prev) => prev + 1).
  • MUST: Keep state minimal — derive everything you can during render instead of syncing with effects.
  • SHOULD: Colocate state as close to where it's used as possible — lift only when sharing is needed.
  • SHOULD: Use useReducer for complex state with multiple sub-values or when next state depends on previous.
  • SHOULD: Use useRef for mutable values that should not trigger re-renders (timers, DOM nodes, previous values).
  • NEVER: Mutate state directly — always use the setter: setState(newValue).
  • NEVER: Store derived data in state — compute it during render.

Effect Taxonomy

Before writing useEffect, classify the work and pick the right approach:

  • MUST: Use computed values during render when the result is pure derived state from props/state. If no external system is touched, it is not an effect.
  • MUST: Use event handlers when work happens because a user clicked, submitted, or navigated. Do not watch a flag in an effect to trigger imperative logic.
  • MUST: Return a cleanup function from effects that set up subscriptions, timers, or listeners.
  • MUST: Keep useEffect single-purpose — one effect, one external synchronization. Split mixed-responsibility effects.
  • SHOULD: Use empty dependency array [] for setup/teardown that should run once on mount/unmount.
  • SHOULD: Reset component state by changing the key prop instead of writing a "watch prop, clear state" effect.
  • SHOULD: Normalize/validate state at the write boundary (event handler, reducer), not via a repair effect.
  • NEVER: Use useEffect just to sync one piece of state to another — find the single source of truth.
  • NEVER: Use useEffect as an event handler (watching a state flag to run a command). Call the action directly from the event source.
  • NEVER: Fetch data in useEffect without handling race conditions (stale closures, unmounted components). Prefer React 19 use() with Suspense or a data-fetching library.

Props & Composition

  • MUST: Destructure props in the function signature: const MyComponent = ({ title, children }: Props) => ....
  • MUST: Use children for composition instead of prop drilling deeply nested config.
  • SHOULD: Spread remaining props onto the root element: const { title, ...rest } = props.

Hooks

  • MUST: Call hooks at the top level — never inside conditions, loops, or nested functions.
  • MUST: Only call hooks from React components or custom hooks.
  • MUST: Prefix custom hooks with use — e.g. useWindowSize, useDebounce.
  • SHOULD: Extract repeated hook logic into custom hooks.
  • NEVER: Call hooks conditionally — this breaks React's hook ordering.

JSX & DOM

  • MUST: Use className not class.
  • MUST: Use htmlFor not for.
  • MUST: Provide a stable, unique key for list items — never use array index as key unless the list is static and never reordered.
  • MUST: Use onSubmit with event.preventDefault() for forms.
  • SHOULD: Use fragments <>...</> to avoid unnecessary wrapper divs.
  • SHOULD: Use style={{ color, "--css-var": value }} for inline styles (camelCase properties).
  • SHOULD: Use aria-* attributes for accessibility.
  • NEVER: Use dangerouslySetInnerHTML without sanitizing input.

Testing

Run checks before committing:

nr build
nr lint

Development Instructions

This is a Next.js 16 app with React 19, Primer React, and Tailwind CSS.

Key Commands

  • Install: ni
  • Dev: nr dev
  • Build: nr build
  • Lint: nr lint