Index
Feed
Filed by fern.dev · May 24, 2026
✓ Fastest verified fix
Make the wrong pattern fail loudly and describe the right one where every agent reads it. An ESLint boundary rule blocks new 'use client' in page/layout/route files, and an AGENTS.md section shows the leaf-extraction pattern so the agent reaches for it first.
✓ What worked
The combination matters: the lint rule alone made agents fight the linter; the recipe alone decayed after compaction. Together, correct extraction in roughly 9 of 10 tasks, and CI catches the tenth.
fern.dev · 2mo ago
size-limit with a per-route budget. Catches the 40KB regressions even when the boundary rule misses a case (e.g. 'use client' added to an existing shared component). Blunt but effective.
clara.builds · 2mo ago
✕ What did not work
One line in AGENTS.md with no pattern and no enforcement. Agents agreed enthusiastically and kept doing the file-top 'use client'. A preference without a recipe changes nothing.
vspaan · 2mo ago
| Agent | Version | Model | OS | Framework | Outcome | × |
|---|---|---|---|---|---|---|
| Claude Code | 2.4.0 | Claude Sonnet 5 | macOS 15.5 | Next.js | Reproduced | 11 |
| Codex | 0.45.1 | GPT-5.5 Codex | macOS 15.5 | Next.js | Reproduced | 6 |
| Cursor | 1.18.3 | GPT-5.5 | Windows 11 | Next.js | Reproduced | 4 |
Sign in to add your report — every count here is backed by a named account.
// eslint.config.mjs
{
files: ["app/**/page.tsx", "app/**/layout.tsx"],
rules: {
"no-restricted-syntax": ["error", {
selector: "ExpressionStatement > Literal[value='use client']",
message: "Pages and layouts stay server components. Extract a client leaf (see AGENTS.md > Interactivity)."
}]
}
}## Interactivity in App Router
When a server component needs an event handler:
1. Create `<Name>Client.tsx` next to it containing ONLY the interactive element.
2. Add 'use client' to that new file, nowhere else.
3. Pass data down as serializable props. Never move fetch() into the client component.
Never add 'use client' to page.tsx, layout.tsx, or any file that imports server-only modules.npx eslint app/ --rule-filter no-restricted-syntaxRelated records
Strong evidence gets promoted into the record above.
+1 on the repro, and a junior-dev data point: I learned the extraction pattern from the agent after the AGENTS.md recipe went in. It now explains why it's creating the leaf file. The same tool that spread the anti-pattern teaches the fix if you feed it the right pattern.
Watch for the variant where it adds 'use client' to an existing shared component instead of the page — the boundary rule as written doesn't catch that, and one shared atom going client can drag a whole subtree with it. Clara's bundle budget was what caught it for us.
Right — that's why the fix lists the budget as a backstop. I've extended our rule to components/ui/** as well now, with an allowlist for the genuinely interactive atoms. Will update the snippet when it's been running for a couple of weeks.
Sign in to join the discussion, vote, and verify fixes.
The lint-message-as-agent-instruction trick generalizes and deserves its own recipe post. Error messages are the one channel that survives compaction, model switches, and vendor differences — every agent reads the error it just caused.