nextjs-app-architecture — скилл для Claude Code: архитектура Next.js
Скилл задаёт единую архитектуру App Router в Next.js 16: страницы только компонуют, данные читают фичи. Пригодится при старте проекта и при разборе чужого кода.
npx skills add aurorascharff/nextjs-app-architecture-skill --skill nextjs-app-architecture -a claude-codeЧто делает nextjs-app-architecture
Скилл ведёт агента по шагам: сначала выбрать режим — собрать приложение с нуля или провести аудит существующего, потом разложить работу по фичам, написать запросы и экшены, собрать компонент со скелетоном. Каждый шаг ссылается на справочник в references/ — по фичам, запросам и экшенам, кэш-компонентам, одностраничным приложениям. Механику фреймворка скилл не пересказывает, а отправляет за ней в документацию Next.js.
Внутри 11 инвариантов, которым должно соответствовать любое изменение. Страницы и лейауты остаются синхронными через params.then(), фичи получают id или slug вместо сырых params, запросы лежат в <domain>-queries.ts с import 'server-only', экшены — в <domain>-actions.ts с 'use server'. Границу <Suspense> ставит страница, а скелетон живёт в том же файле, что и компонент.
Перед первым запуском нужно подготовить проект: скилл требует, чтобы у агента был доступ к документации под свою версию Next.js — через AGENTS.md или встроенные доки, и советует создать их, если их нет. Без этого шага агент будет опираться на память, а версии API в Next.js 16 расходятся с прежними. Часть ссылок ведёт на preview.nextjs.org, то есть на canary-API.
Какие задачи решает
- разложить страницы App Router по фичам, а не по маршрутам
- переписать страницу-загрузчик в асинхронный серверный компонент
- решить, где живут запросы, экшены и компоненты
- расставить границы Suspense и не потерять статичный каркас
- вынести скелетоны так, чтобы не прыгала вёрстка при загрузке
Примеры запросов агенту
- проведи аудит app/ и покажи, где страницы сами делают запросы
- добавь фичу избранного по этой архитектуре
- включи Cache Components и расставь теги кэша для ленты
Как установить nextjs-app-architecture
1Через Skills CLI (Vercel)
npx skills add aurorascharff/nextjs-app-architecture-skill --skill nextjs-app-architecture -a claude-code2Вручную: положить файлы
~/.claude/skills/nextjs-app-architecture/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/nextjs-app-architecture/
1Через Skills CLI (Vercel)
npx skills add aurorascharff/nextjs-app-architecture-skill --skill nextjs-app-architecture -a codex2Вручную: положить файлы
.agents/skills/nextjs-app-architecture/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add aurorascharff/nextjs-app-architecture-skill --skill nextjs-app-architecture -a cursor2Вручную: положить файлы
.cursor/skills/nextjs-app-architecture/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install aurorascharff/nextjs-app-architecture-skill nextjs-app-architecture2Вручную: положить файлы
.github/skills/nextjs-app-architecture/SKILL.md1Через Skills CLI (Vercel)
npx skills add aurorascharff/nextjs-app-architecture-skill --skill nextjs-app-architecture -a gemini-cli2Командой платформы
gemini skills install https://github.com/aurorascharff/nextjs-app-architecture-skillВручную: положить файлы
.devin/skills/nextjs-app-architecture/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add aurorascharff/nextjs-app-architecture-skill --skill nextjs-app-architecture -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Вопросы о nextjs-app-architecture
Как установить nextjs-app-architecture в Claude?
Выполните npx skills add aurorascharff/nextjs-app-architecture-skill --skill nextjs-app-architecture -a claude-code. Скилл встанет в ~/.claude/skills/nextjs-app-architecture/SKILL.md.
Что делает nextjs-app-architecture?
Задаёт порядок работы над приложением на Next.js 16 App Router: где размещать фичи, как писать запросы и экшены, как ставить Suspense и скелетоны. Работает и при сборке с нуля, и при разборе существующего кода.
Что нужно подготовить до запуска скилла?
Проект должен быть настроен так, чтобы агент читал документацию под свою версию Next.js — через AGENTS.md или встроенные доки. Скилл советует создать или обновить их, если их нет.
Исходный текст
SKILL.md · MIT
---
name: nextjs-app-architecture
description: Build or audit Next.js 16 App Router apps using a next-beats-style React Server Components architecture. Use when scaffolding a new app, adding a feature, reviewing an existing app, refactoring route-loader-shaped pages into feature-owned async server components, deciding where queries/actions/components live, keeping pages synchronous with `params.then()`, placing Suspense boundaries, choosing the client/server boundary, designing skeletons, preventing CLS, or enabling Cache Components. Also use when the user asks about RSC composition, components receiving IDs instead of route params, `'use cache'`, `cacheTag`, `updateTag`, static-shell prerendering, or making an app easier for AI agents to modify.
license: MIT
metadata:
author: aurorascharff
version: "1.4.1"
---
# Next.js App Architecture
A workflow for building and auditing Next.js 16+ App Router apps so they follow one consistent, feature-sliced RSC architecture like `next-beats`.
**Follow the workflow below step by step** — it produces the invariants by construction. Load the reference a step names for the decision it depends on. Get framework _mechanics_ (API signatures, config options, hook contracts) from the linked docs — don't restate or improvise them. Links for canary-only Next.js APIs use `preview.nextjs.org`.
## Prerequisite
Before changing a Next.js app, make sure the project is set up for AI agents to read version-matched docs. Follow the [AI Coding Agents guide](https://preview.nextjs.org/docs/app/guides/ai-agents): prefer the project's `AGENTS.md` / bundled docs, and create or refresh them when missing. Then use this skill for architecture decisions.
## Architecture target
Build pages that describe the loading experience, not pages that act like route loaders:
- `app/**/page.tsx` and `layout.tsx` are synchronous composition surfaces: static chrome, section headings, `<Suspense>` boundaries, error boundaries, and transition wrappers.
- Feature components own their reads on the server. They receive minimal stable inputs (`id`, `slug`, `handle`, parsed filter values) or already-fetched records, never raw `params` / `searchParams`.
- Queries and actions live in the feature folder. Components import queries; client leaves import actions directly.
- When server tags and client query keys describe the same feature data, a pure feature-local cache contract owns those identities.
- Stable chrome, wrappers, and skeletons preserve layout: cards/panels stay outside Suspense, and fallbacks swap only the data-dependent body.
## Invariants (what every change must satisfy)
The non-negotiables. The workflow produces them; the final check verifies them.
1. **Pages compose, they never fetch.** A page/layout imports feature components and places `<Suspense>`. No queries or domain logic inline. Tiny route-local control-flow helpers are allowed only when they exist to place a boundary around `connection()`, `redirect()`, or a resolved route prop.
2. **Pages stay synchronous.** Use `params.then()` / `searchParams.then()` or `Promise.all([params, searchParams]).then(...)`, never `await params` at the top — so chrome paints into the static shell and only data-dependent sections suspend.
3. **Feature components receive IDs, not route props.** Resolve `params` / `searchParams` at the page boundary and pass plain values (`id`, `slug`, `query`) into features.
4. **Async server component is the default.** `'use client'` only for hooks, event handlers, or browser APIs — and only on leaves, never on parents of server content.
5. **The page owns the Suspense boundary; the feature owns the skeleton.** Features never pre-wrap themselves in `<Suspense>`; stable wrappers/cards/chrome wrap the boundary instead of being duplicated in fallback and final content.
6. **Skeletons live in the same file as the component**, exported alongside it, defined at the end. `Feed` and `FeedSkeleton` are siblings.
7. **Queries live in `<domain>-queries.ts`** (`import 'server-only'`); **actions live in `<domain>-actions.ts`** (`'use server'`). The file name matches the folder, even for sub-concepts.
8. **Feature folders follow product ownership.** Entity-owned sub-concepts (favorite, like, vote, bookmark) fold into their parent. A route-level experience that composes multiple domains may own its UI and state in a separate feature while each domain keeps its queries and actions.
9. **Client components import actions directly** — never receive a server action as a prop just to call it.
10. **Feature-local cache coordination stays with its domain.** Put pure tags/keys in `<domain>-cache.ts`, client query definitions in `<domain>-query-options.ts`, hook wrappers in `hooks/use-*.ts`, and tiny client leaves in `components/`; promote support code only after real cross-feature reuse.
11. **Interactive async UI keeps server data on the server and client state local to the interaction.** Use `useOptimistic`, `useTransition`, reducers, URL/search params, and form actions instead of mirrored prop state, derived-state effects, or hand-rolled pending arrays.
## Workflow
Run these in order for build-from-scratch, feature work, or audits. Each step names the reference to consult and the check it must pass.
1. **Choose mode.**
- **Build from scratch:** sketch routes, real domain nouns, static shell, and expected loading groups before writing code.
- **Audit/refactor:** scan current `app/` pages first; list every async page, page-level query import, route prop leak, missing Suspense boundary, and feature folder mismatch.
→ `references/example.md` for the target shape; `references/feature-folders.md` for placement.
✓ You know whether you are creating the architecture or converting loader-shaped code into it.
2. **Place the work.** Decide the feature folder before writing anything.
→ `references/feature-folders.md` (decision tree + merge rules).
✓ A real domain, a cross-domain product experience, or folded into the right parent.
3. **Write the query and, when a client cache shares its data, the cache contract.** Put server reads in `<domain>-queries.ts` with `import 'server-only'`; keep shared tag/key identities in a pure `<domain>-cache.ts`.
→ `references/queries-actions.md`; for SWR/TanStack Query → `references/single-page-applications.md`; with `cacheComponents: true`, also → `references/cache-components.md`.
✓ Cache identities are defined once; server reads are server-only, cached/tagged/lifetimed under Cache Components, and return domain types rather than ORM rows.
4. **Write the action** (if there's a mutation). `features/<domain>/<domain>-actions.ts`, `'use server'` at the top.
→ `references/queries-actions.md`.
✓ Re-checks auth, validates input, invalidates matching cache tags under Cache Components (`refresh()` only for justified dynamic reads), returns a discriminated union.
5. **Build the component + skeleton.** `features/<domain>/components/<name>.tsx`: an async server component that awaits its own query from minimal props; `'use client'` only on interactive leaves.
→ `references/components.md`; for a client data library or strict-SPA/CSR feature → `references/single-page-applications.md`.
✓ Component receives IDs/handles/parsed filters or already-resolved records, not `params`; skeleton is a sibling export at the end; no alias skeleton wrappers.
6. **Compose the page.** `app/<route>/page.tsx`: synchronous, `params.then()` / `Promise.all(...).then(...)`, place Suspense around data bodies, and wrap the section that can fail independently in an error boundary.
→ `references/pages-suspense.md`; with `cacheComponents: true`, also → `references/instant-navigation.md` for which links prefetch and which reads wait for the navigation.
✓ Route props become plain values; stable cards/sections wrap Suspense when they set layout; boundaries stay visible at the page.
7. **Add interaction** (if any): optimistic updates, pending state, toasts, confirmation.
→ `references/ux-patterns.md`.
✓ Feedback isn't doubled; optimistic reducers/actions live with the feature; URL/search params own shareable state; client effects synchronize external systems, not derived React state.
8. **Verify** against the checklist below before declaring done. When the change touches a loading sequence, lock it in with an end-to-end test.
→ `references/testing.md`.
✓ The test asserts which content is available before uncached work resolves, using visibility-aware locators.
## Verify before done
Inspect the diff against every invariant — each is checkable by reading the changed files:
- [ ] No page/layout imports a `*-queries` file or defines reusable domain UI inline; any inline helper is route-local control flow only.
- [ ] Every page with params is synchronous and uses `params.then()` / `searchParams.then()` / `Promise.all([params, searchParams]).then(...)`.
- [ ] Feature components receive plain IDs/handles/parsed filters or resolved records; no feature prop is named `params` or `searchParams`.
- [ ] Every `<Suspense>` for page data sits in the page; no feature pre-wraps itself.
- [ ] Stable wrappers/cards/chrome sit outside Suspense; fallback and final content do not duplicate the same outer card.
- [ ] Every component has its real `*Skeleton` in the same file, at the end; no tiny skeleton aliases just to pass props.
- [ ] Skeleton and content are the same height (measured), section headings sit outside the boundary, and sections that can be empty reserve their space.
- [ ] Sections below a variable-height section are nested sibling boundaries inside it, not awaited by it and not passed through it as children.
- [ ] `unstable_prefetch()` / `unstable_navigation()` are awaited in the component before its query call; no wrapper query exists just to hold the gate.
- [ ] Every `*-queries.ts` starts with `import 'server-only'`; every `*-actions.ts` with `'use server'`.
- [ ] With `cacheComponents: true`, reusable reads use `'use cache'` / `cacheTag` / `cacheLife`, or `'use cache: private'` / `'use cache: remote'` when appropriate; any dynamic read is intentional and justified.
- [ ] A personalized shared-cache scope receives trusted, normalized identity values from an uncached feature wrapper; it never reads request APIs or trusts a client-provided identity inside the cache scope.
- [ ] Reads whose every write calls `updateTag()` use `cacheLife('max')`; a shorter profile is reserved for data that changes outside the app's actions.
- [ ] Mutations touching cached reads call `updateTag()` / `revalidateTag(..., 'max')` for the matching tags; `refresh()` is not a substitute for tag invalidation.
- [ ] `prefetch={true}` is reserved for links users predictably follow, or for rows whose per-link prefetch was made cheap by gating the heavy part behind `unstable_navigation()`.
- [ ] Action files are named `<folder>-actions.ts`; no entity-owned sub-concept spawned its own folder, and cross-domain product features do not take ownership of entity queries/actions.
- [ ] Features with both server tags and client query keys define them once in a pure `<domain>-cache.ts`; queries, actions, hydration, query options, and hooks import from it.
- [ ] Feature-local client-support files sit in the smallest fitting place: query options at the feature root, `use-*` hook wrappers in `hooks/`, leaf components in `components/`, and shared support only after real cross-feature reuse.
- [ ] `'use client'` components are leaves — they import actions/hooks/providers, not async server components. A client component that needs server data has a server half in the feature (`ComposePanel` → `ComposePanelClient`); no page or layout creates query promises for it.
- [ ] Client leaves use `useOptimistic`, transitions, reducers, URL state, or form actions for interaction; they do not call `setState` in effects for derived React state.
- [ ] Forms whose action can reject input keep what the user typed: the action state returns the submitted values and the fields read them as `defaultValue`.
- [ ] Mutations validate their input and invalidate the affected data.
## Reference index
- **`references/feature-folders.md`** — where code goes: folder layout, cache contracts, naming, and merging sub-concepts.
- **`references/queries-actions.md`** — query/action rules: server-only, dedup, validation, invalidation, return shape.
- **`references/components.md`** — server/client boundary, skeletons, `use()`, single-use helpers, live data.
- **`references/pages-suspense.md`** — page composition, `params.then()`, Suspense placement, CLS, error boundaries.
- **`references/instant-navigation.md`** — prefetching decisions: `prefetch={true}`, the three render stages and their gates, validating what a navigation shows, hidden routes.
- **`references/cache-components.md`** — the `cacheComponents` decisions: which reads to cache, which directive to use, how to invalidate.
- **`references/single-page-applications.md`** — client cache decisions: placement, server seeding, Cache Components coordination, hydration, and mutations.
- **`references/ux-patterns.md`** — interaction decisions: optimistic vs pending vs inline error, toasts, action-prop, confirmations.
- **`references/testing.md`** — end-to-end tests: asserting the loading sequence with `instant()`, locating content across hidden routes, testing mutations.
- **`references/example.md`** — the next-beats reference app: invariant → file map, for seeing any rule in real code.
© aurorascharff · MIT · aurorascharff/nextjs-app-architecture-skill