Next.js, React and TypeScript rules for AI coding agents

A coding agent that learned Next.js from older tutorials will often write getServerSideProps, import useRouter from next/router, or put "use client" on a whole layout to silence an error. None of that belongs in an App Router project. These rules are written for the App Router with React Server Components and strict TypeScript, and they spell out where the server and client boundary goes, how fetching and caching should work, and how mutations are handled.

The part teams tend to miss is security: the rules treat every "use server" function as a public POST endpoint that must parse its input with a schema and check the session and permission inside the action. On the React side they push the agent to derive values during render instead of syncing props into state with useEffect.

Folder: rules/nextjs-react-ts/ · nextjs-react-ts.mdc, CLAUDE.md, AGENTS.md

What the Next.js rules tell your agent

First rule of each section in nextjs-react-ts.mdc, quoted as shipped.

Mistakes these rules are written to prevent

Source: “Pitfalls to avoid”.

Checks the agent runs before it says done

Example commands from the Next.js file (swap in your repo's own):

Run the type check (npx tsc --noEmit), the project's ESLint command and the unit tests. Run next build when you changed routing, caching or a client/server boundary. Report what you ran and what failed. Never claim a build passes without running it.

Before and after: React: remove state that is synced with useEffect

Prompt card 04 · Stack: React (any framework) · Type: Refactor · Best model: a fast small model is usually enough

What the card is for

Replace useState + useEffect pairs that copy or compute data from props or other state. Derived values computed during render are always in sync, cause one render instead of two, and remove a class of stale-state bugs.

Worked example from the card

Before

function ContactList({ contacts, query }: { contacts: Contact[]; query: string }) {
  const [visible, setVisible] = useState<Contact[]>([]);
  const [count, setCount] = useState(0);
  useEffect(() => {
    const v = contacts.filter(c => c.name.toLowerCase().includes(query.toLowerCase()));
    setVisible(v);
    setCount(v.length);
  }, [contacts, query]);
  return <><p>{count} contacts</p>{visible.map(c => <Row key={c.id} contact={c} />)}</>;
}

Expected AI output (abridged)

- visible + effect: derived from contacts and query. Compute during render.
- count: derived from visible.length. Remove the state.
- Cost: filtering a few hundred items per keystroke is cheap; no useMemo needed.
  If contacts can be in the tens of thousands and the compiler is off, wrap the filter in
  useMemo keyed on [contacts, query].
- No effect remains; the first render now shows the right list (before, it showed
  "0 contacts" for one frame).

After

function ContactList({ contacts, query }: { contacts: Contact[]; query: string }) {
  const needle = query.toLowerCase();
  const visible = contacts.filter(c => c.name.toLowerCase().includes(needle));
  return (
    <>
      <p>{visible.length} contacts</p>
      {visible.map(c => <Row key={c.id} contact={c} />)}
    </>
  );
}

Excerpt from card 04; the prompt itself is in the pack.

More Next.js prompt cards in the pack

Using these rules in Cursor, Claude Code and AGENTS.md tools

Cursor

Put _base.mdc and nextjs-react-ts.mdc in .cursor/rules/. Globs: app/**/*.tsx, app/**/*.ts, src/**/*.tsx, src/**/*.ts, components/**/*.tsx, middleware.ts, proxy.ts, next.config.*.

Claude Code

Root CLAUDE.md, base first. Path-scoped variant .claude/rules/nextjs-react-ts.md for app/**/*.{ts,tsx}, src/**/*.{ts,tsx}, components/**/*.tsx; PostToolUse hook: npx prettier --write <file>.

AGENTS.md tools (Codex, Gemini CLI, Aider)

Base then stack in one AGENTS.md. Gemini CLI needs context.fileName; Aider needs --read AGENTS.md. Put this file in apps/web/ in a monorepo, or the repo root for a single app. Agents use the AGENTS.md nearest to the file being edited, so a nested copy overrides a root one for that subtree.

Related stacks

← All stacks