Next.js is convention over configuration. Knowing the conventions saves you from reading docs every time you add a feature.
| Folder | Purpose |
|---|---|
app/ | App Router routes, layouts, pages, server logic |
public/ | Static assets served at the URL root |
components/ | (You create) shared React components |
lib/ | (You create) helpers, db clients, server utilities |
styles/ | (You create) CSS files if not co-located |
app/¶These file names have superpowers. Every other file is just a regular module.
| File | Purpose | Runs On |
|---|---|---|
page.tsx | Renders the URL | Server (default) |
layout.tsx | Wraps a route segment + its children | Server (default) |
template.tsx | Like layout but re-mounts on navigation | Server |
loading.tsx | Suspense fallback for the segment | Server |
error.tsx | Error boundary for the segment | Client |
global-error.tsx | Catches errors in the root layout | Client |
not-found.tsx | Renders for notFound() calls | Server |
route.ts | HTTP endpoint (GET/POST/...) | Server |
middleware.ts | Edge middleware (project root) | Edge runtime |
| Pattern | URL | Notes |
|---|---|---|
app/about/page.tsx | /about | Static segment |
app/blog/[slug]/page.tsx | /blog/:slug | Dynamic segment |
app/shop/[...path]/page.tsx | /shop/* | Catch-all |
app/shop/[[...path]]/page.tsx | /shop & /shop/* | Optional catch-all |
app/(marketing)/page.tsx | / | Route group (no URL effect) |
app/@modal/page.tsx | — | Parallel route slot |
app/(.)photo/[id]/page.tsx | Intercepts /photo/:id | Intercepting route |
app/_internal/util.ts | (none) | Underscored → private, never routed |
A page.tsx must default-export a React component:
1// app/about/page.tsx
2export default function AboutPage() {
3 return <h1>About</h1>;
4}A layout.tsx must accept { children }:
1// app/blog/layout.tsx
2export default function BlogLayout({ children }: { children: React.ReactNode }) {
3 return (
4 <div className="grid grid-cols-[200px_1fr]">
5 <Sidebar />
6 <main>{children}</main>
7 </div>
8 );
9}A route.ts exports HTTP verbs:
1// app/api/health/route.ts
2export async function GET() {
3 return Response.json({ status: "ok" });
4}You can place any non-special file inside app/ — components, tests, hooks, CSS. Only files with reserved names participate in routing.
app/
blog/
page.tsx
PostCard.tsx ← regular component
PostCard.test.tsx ← test (never routed)
post-card.module.css
| File | Result |
|---|---|
favicon.ico (root of app/) | Site favicon |
opengraph-image.png | OG image for the segment |
robots.txt / robots.ts | Generated robots rules |
sitemap.xml / sitemap.ts | Generated sitemap |
manifest.json / manifest.ts | PWA manifest |
Drop the file in the right place — Next does the wiring.
app/
(marketing)/
layout.tsx
page.tsx
pricing/page.tsx
(app)/
layout.tsx ← requires auth
dashboard/
page.tsx
loading.tsx
courses/
[slug]/
page.tsx
@reviews/page.tsx
api/
auth/[...nextauth]/route.ts
webhooks/stripe/route.ts
layout.tsx ← root layout
globals.css
components/
ui/ ← primitives (button, input, dialog)
feature/ ← domain components
lib/
auth.ts
db.ts
validations/
middleware.ts
This structure scales from prototype to 200 KLOC apps without restructuring.