- MUST: Use @antfu/ni. Use
nito install,nr SCRIPT_NAMEto run.nunto 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
innerXinstead ofx - Example: instead of
movedusedidPositionChange
- Example: for .map(), you can use
- 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.tsusingSCREAMING_SNAKE_CASEwith unit suffixes (_MS,_PX). - MUST: Put small, focused utility functions in
utils/with one utility per file. - MUST: Use Boolean over !!.
- MUST: This project uses React Compiler — never manually use
useMemo,useCallback,React.memo, orforwardRef. The compiler handles memoization automatically.
- 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
useEffector event handlers. - MUST: Understand the render cycle: state change → re-render → commit to DOM → effects run.
- MUST: Use
useStatefor 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
useReducerfor complex state with multiple sub-values or when next state depends on previous. - SHOULD: Use
useReffor 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.
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
useEffectsingle-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
keyprop 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
useEffectjust to sync one piece of state to another — find the single source of truth. - NEVER: Use
useEffectas an event handler (watching a state flag to run a command). Call the action directly from the event source. - NEVER: Fetch data in
useEffectwithout handling race conditions (stale closures, unmounted components). Prefer React 19use()with Suspense or a data-fetching library.
- MUST: Destructure props in the function signature:
const MyComponent = ({ title, children }: Props) => .... - MUST: Use
childrenfor composition instead of prop drilling deeply nested config. - SHOULD: Spread remaining props onto the root element:
const { title, ...rest } = props.
- 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.
- MUST: Use
classNamenotclass. - MUST: Use
htmlFornotfor. - MUST: Provide a stable, unique
keyfor list items — never use array index as key unless the list is static and never reordered. - MUST: Use
onSubmitwithevent.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
dangerouslySetInnerHTMLwithout sanitizing input.
Run checks before committing:
nr build
nr lintThis is a Next.js 16 app with React 19, Primer React, and Tailwind CSS.
- Install:
ni - Dev:
nr dev - Build:
nr build - Lint:
nr lint