Next.js App Router architecture checklist
A practical checklist for planning routes, layouts, loading states, error boundaries, data access, authentication, and navigation before a Next.js App Router project becomes expensive to rearrange.
Start with URLs, not folders
A Next.js App Router project eventually becomes a folder tree, but the useful design question comes first: which URLs should exist, and what should a person be able to accomplish at each one?
Write the public route contract before creating folders. Include static pages, dynamic segments such as /projects/[id], optional search parameters, and routes that exist only after authentication. Give every route one clear job. When two pages have the same job, decide whether they are genuinely separate destinations or two views of one route.
The free Next.js structure painter turns that list into a visual route tree and downloads the result as an App Router skeleton.
Decide layout ownership
For each branch, identify the UI inherited by every child:
- Global document shell, fonts, analytics, and providers belong in the root layout.
- Marketing navigation belongs in a marketing route-group layout.
- Account navigation belongs above the account subtree.
- A project toolbar belongs above project pages, not inside every individual page.
Layouts persist across navigation. That makes them useful for stable chrome and dangerous for state that should reset between pages. Record which state is intentionally persistent before placing a provider in a layout.
Route groups organize folders without changing the URL. Use them to separate shells or ownership boundaries, not merely to make the tree look tidy.
Mark server and client boundaries
Treat Server Components as the default. Mark the smallest interactive leaf as a Client Component when it needs browser APIs, local state, event handlers, or a client-only library.
For each page, answer:
- What data must exist before the page can render?
- Can that data be read on the server?
- Which interactions require client state?
- Does a client provider force more of the tree into the browser than intended?
A clear boundary reduces JavaScript, prevents secret-bearing modules from entering client bundles, and makes metadata easier to render for crawlers.
Plan every route state
The happy path is only one version of a route. Decide how each subtree behaves while loading, when data is missing, and when rendering fails.
loading.tsxshould represent the shape of the arriving content.error.tsxshould offer a recovery action and log enough context to diagnose the failure.not-found.tsxshould be used when the requested resource truly does not exist.- Empty states belong in the page itself because an empty collection is usually a successful response.
Keep error boundaries close enough that a failed panel does not erase unrelated navigation, but high enough that repeated routes do not duplicate the same recovery UI.
Put authorization at the data boundary
A hidden link is not access control. Neither is a redirect performed only by middleware or client code.
List the roles that may read or change each protected resource, then enforce that rule in the server-side operation that touches the data. Middleware can improve the signed-out experience, but the route handler, server action, or data-access function must make the final decision.
Plan the difference between authentication and authorization: “has a session” and “may edit this project” are separate questions.
Draw navigation separately from hierarchy
Folder ancestry describes layout inheritance. It does not describe every path a person can travel. A dashboard card may link to a deeply nested editor; a successful form may return to a sibling; a settings page may link back to billing.
Draw those transitions and check:
- Is every important page reachable without manually editing the URL?
- Does each task have an obvious way back?
- Are redirects deterministic after sign-in and form submission?
- Do guarded destinations preserve the intended return path?
Finish with metadata and tests
Before implementation, give every indexable route a working title, description, canonical URL, and sharing image. Mark private and utility pages noindex. Add acceptance cases beside the route: what should render, who may enter, what happens with invalid parameters, and where a successful action leads.
The architecture is ready when the route tree, layout ownership, data boundaries, navigation paths, metadata, and failure states agree. At that point the folders are no longer a guess—they are the code-shaped expression of a plan.
Application architecture
Continue this learning path
Route trees, layouts, navigation, guards, and the structural decisions that become expensive after implementation begins.
Design your application
Carry the route tree into a complete project with data, APIs, access rules, tests, and generated scaffolding.
Open projects