On this page
This is the start of every project I ship. Follow it top to bottom and you will end with a running Next.js app that is already structured the way the rest of this handbook assumes. The goal is a boring, repeatable start, because a boring start is what lets the interesting work stay fast later.
The path
Create the app
Run the official generator. Do not click through the prompts blindly; the flags below are the choices that matter.
npx create-next-app@latest my-app --typescript --eslint --app
cd my-app--typescriptis not optional for me. The first time a refactor would have silently broken three call sites, types pay for themselves.--appselects the App Router, which gives you Server Components by default. That is the single biggest lever for shipping less JavaScript to the browser.--eslintwires up linting from the first commit, so problems surface while they are cheap to fix.
Run it and confirm
npm run devOpen http://localhost:3000. You should see the starter page. If the dev server starts and the page loads, your toolchain is healthy and you can build on it.
Settle the folder shape
Decide where things live now, because retrofitting structure hurts. The rule:
app/ is for routing only. Pages and layouts stay thin. Real logic lives in reusable modules they import.
A shape that scales:
app/ routes, layouts, route handlers only
components/ reusable UI
lib/ data access, validation, integrations, types
content/ markdown / data, kept out of app/
public/ static assetsThe point is that app/ never becomes the place business logic hides. When you need to find how something works, you look in lib/, not in a route file.
Write your first route the secure way
Server Components are the default in the App Router. Use that: fetch data on the server, send the browser only what it needs.
// A Server Component by default. No "use client" here.
export default async function Page() {
const message = await getWelcome();
return <h1>{message}</h1>;
}
async function getWelcome() {
// Runs on the server. Secrets, database calls, and heavy work stay here.
return "Hello from the server";
}When you genuinely need interactivity, push "use client" as far down the tree as possible:
Avoid
"use client"; // now the whole page ships to the browser
export default function Page() {
/* ... */
}Prefer
"use client"; // only this button is client code
export function LikeButton() {
/* ... */
}