Key Takeaways
- Next.js supplies routing, rendering, build tooling, and server commands so you don't have to assemble them yourself.
- Start new projects with the App Router. The Pages Router, including
pages/about.jsandpages/api/books.js, remains supported. - App Router pages and layouts are Server Components by default; use Client Components for state, event handlers, and browser APIs.
- Development and production are different workflows: run
next buildsuccessfully before starting the standard production server withnext start.
Next.js is a React framework that reduces the setup needed to build applications with pages, server-side code, and static assets. Its filesystem conventions connect your files to URLs, while its integrated toolchain handles compilation and bundling.
This Next.js tutorial for beginners builds a minimal starter with an About page, a dynamic book page, and a JSON endpoint. You'll use the App Router first, then see the original Pages Router equivalents clearly labeled.
Create a Next.js Starter App
Use Node.js 24 LTS for this starter. Next.js documents Node.js 20.9 as its minimum, but that compatibility floor isn't a recommendation to install Node.js 20, which is now end-of-life. Check the Node.js release status when choosing your runtime.
The examples pin create-next-app to 16.4.2, matching the official release reviewed for this October 17, 2026 refresh. The pin makes the scaffolding instructions more reproducible; keep application dependencies patched after creating the project.
npx create-next-app@16.4.2 your-app --use-npmChoose customize settings rather than accepting the recommended defaults. Select these options for the JavaScript examples below:
- TypeScript: No.
- Linter: ESLint.
- React Compiler and Tailwind CSS: No.
- Code inside a
src/directory: No. - App Router: Yes.
- Cache Components: No, to keep this first example focused on routing.
- Import alias: keep
@/*.
The CLI installs dependencies and creates files according to your choices. These are selected files and directories, not an exhaustive generated tree:
your-app/
├── app/
│ ├── globals.css
│ ├── layout.js
│ └── page.js
├── public/
├── eslint.config.mjs
├── jsconfig.json
├── next.config.mjs
├── node_modules/
├── package-lock.json
└── package.jsonStart development:
cd your-app
npm run devOpen http://localhost:3000. The default port is 3000; use the address printed in your terminal if development selects another available port.
Development, build, and production commands
This is a scripts excerpt to merge into the generated package.json, not a replacement for the complete file:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
}
}next dev runs the development server; next build creates production output; next start serves that output. Run ESLint separately: next build doesn't run the linter automatically.
To check production behavior locally, stop the development server, then run:
npm run lint
npm run build
npm run startHow Next.js Works
You don't need to configure Babel, Webpack, or Express to follow this tutorial. Next.js provides an integrated toolchain: its compiler is based on SWC, and Turbopack is the default bundler in the release used here. Different framework versions and custom configurations can use different tooling.
- During development: Next.js compiles your application and updates the running development app as you edit.
- During a production build: it compiles production output and prerenders content that can be prepared ahead of requests.
- When a page is requested: it serves prepared content or performs the required server rendering. On an initial App Router load, HTML provides the first view, and React hydrates Client Components—attaches their interactive behavior—in the browser.
Server Components, streaming, and caching make this more flexible than a simple choice between server-side rendering and static generation. Rendering and reuse depend on your route, configuration, and Next.js version.
Next.js Filesystem Routing: App Router vs Pages Router
Both routers derive URLs from files, but their conventions differ. In the App Router, folders define route segments—the pieces between slashes—and page.js supplies a page's UI. A layout.js wraps the pages beneath it.
URL App Router Pages Router
/ app/page.js pages/index.js
/about app/about/page.js pages/about.js
/books/hobbit app/books/[id]/page.js pages/books/[id].js
/api/books app/api/books/route.js pages/api/books.jsThe API row represents HTTP endpoints, not ordinary React pages. In the Pages Router, special files such as _app.js and _document.js customize application behavior and the document rather than creating ordinary public pages.
Creating Pages with Next.js
App Router: a root layout and home page
Replace app/layout.js with this minimal root layout. The root layout must include html and body; children is where the current page appears.
// app/layout.js
import './globals.css';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}Replace app/page.js with the home page:
// app/page.js
import Link from 'next/link';
export default function Home() {
return (
<main>
<h1>Book starter</h1>
<Link href="/about">About us</Link>
</main>
);
}Link provides framework-aware navigation between pages. For shared navigation and nested UI, continue with our Next.js layout examples.
App Router: create the About page
Create app/about/page.js:
// app/about/page.js
export default function About() {
return (
<main>
<h1>About us</h1>
<p>We help readers find their next book.</p>
</main>
);
}Visit /about. Pages and layouts are Server Components by default. For an interactive widget, put 'use client' at the top of its component file before imports; that boundary enables client-side features such as state and event handlers. It doesn't mean the component can never be prerendered on the server.
Pages Router alternative: pages/about.js
In a Pages Router project, save the same About component as pages/about.js. It maps directly to /about:
pages/
├── index.js
└── about.jsYou don't need to migrate an existing Pages Router application just to add this page. The router remains supported.
Create a dynamic book route
A bracketed folder accepts a variable URL segment. Create app/books/[id]/page.js; in the release used here, params is a promise, so await it before reading id.
// app/books/[id]/page.js
import { notFound } from 'next/navigation';
export default async function BookPage({ params }) {
const { id } = await params;
if (id !== 'hobbit') {
notFound();
}
return (
<main>
<h1>The Hobbit</h1>
<p>Author: Tolkien</p>
</main>
);
}Visit /books/hobbit. Other IDs invoke Next.js's not-found handling. The fixture keeps this example self-contained; replace the lookup with your data-access code when you're ready. Our Next.js MongoDB examples cover a database-backed extension.
Creating API Endpoints with Next.js
Pages Router API route: pages/api/books.js
A file under pages/api/ creates a server-side API endpoint. This compact handler preserves the original book response while checking the request method and an optional id query parameter.
// pages/api/books.js
export default function handler(req, res) {
if (req.method !== 'GET') {
res.setHeader('Allow', 'GET');
return res.status(405).json({ error: 'Method not allowed' });
}
const { id } = req.query;
if (id !== undefined && typeof id !== 'string') {
return res.status(400).json({ error: 'id must be a single string' });
}
if (id !== undefined && id !== 'hobbit') {
return res.status(404).json({ error: 'Book not found' });
}
return res.status(200).json({
title: 'The Hobbit',
author: 'Tolkien',
});
}Open /api/books or /api/books?id=hobbit. This handler returns 200 for the book, 400 for repeated IDs parsed as an array, 404 for an unknown ID, and 405 for unsupported methods.
There's no fallible database or network operation here. When you add one, catch operational failures, log appropriate details server-side, and return a sanitized 500 response rather than leaking internal errors. Authenticate and authorize protected operations inside the server endpoint.
App Router alternative: a Route Handler
For a new App Router application, use app/api/books/route.js instead. Route Handlers export named HTTP-method functions and use the Web Request and Response APIs. This simplest equivalent returns the fixed book without the optional filter:
// app/api/books/route.js
export function GET() {
return Response.json({
title: 'The Hobbit',
author: 'Tolkien',
});
}Choose one implementation for /api/books. You can retain pages/api/ while using App Router pages elsewhere.
Keep credentials in server-side environment variables, not response bodies or component props. Never prefix a secret with NEXT_PUBLIC_: that prefix allows its value to be included in browser JavaScript. See the environment variable guide.
Serving Static Files and Images
Place an image at public/some-image.png. Its URL is /some-image.png, not /public/some-image.png. A regular image element works:
// JSX fragment inside a component's return value
<img src="/some-image.png" alt="Illustrated cover of The Hobbit" />For image optimization, use next/image. For example, after adding a 640-by-480 image at that path, create this page:
// app/cover/page.js
import Image from 'next/image';
export default function CoverPage() {
return (
<Image
src="/some-image.png"
alt="Illustrated cover of The Hobbit"
width={640}
height={480}
/>
);
}Use the asset's actual dimensions. They establish its aspect ratio and help avoid layout shifts while it loads.
Customizing and Extending Next.js
Start with filesystem routing and add configuration for a concrete requirement. For example, use a redirect when an old public URL should lead to a new one. Merge this into next.config.mjs:
// next.config.mjs
const nextConfig = {
async redirects() {
return [
{
source: '/old-about',
destination: '/about',
permanent: false,
},
];
},
};
export default nextConfig;Here, permanent: false produces a temporary 307 redirect. It changes the browser's destination; it isn't a backend proxy. See the redirect configuration reference.
The original useFileSystemPublicRoutes: false example belongs to specialized Pages Router custom-server setups—for example, preventing a manually mapped page from also appearing at its filename URL. It disables filename-based server rendering routes, but client-side navigation can still reach them. Don't use it as an access-control mechanism or a starter setting.
For styling, Next.js supports global CSS and CSS Modules. To enable Sass integration, install the package first:
npm install --save-dev sassThe Benefits of Using Next.js
- Less integration work: routing, build tooling, and server commands come together instead of requiring a separately assembled stack.
- Smaller route-specific downloads: automatic code splitting separates code by pages or route segments rather than making every visit download the entire application. Large client dependencies still need attention.
- Flexible rendering: server rendering can deliver useful HTML before browser JavaScript finishes, while prerendering can reuse work performed ahead of requests. Neither removes the cost of slow data access.
Server-side rendering isn't an automatic performance win: request-time work adds latency, and interactive components still need browser JavaScript. Compare the trade-offs in our client-side versus server-side rendering guide, and make deliberate caching decisions rather than assuming every response is cached.
Deployment and Production Boundaries
The standard Node.js deployment uses next build followed by next start. Managed hosting can handle that workflow for you; check the platform's supported features. A static export has no Next.js runtime server and can't run Pages Router API routes.
An existing backend can remain a separate service that your Next.js application calls. Express isn't preconfigured, and you generally don't need a custom server. Explore our Next.js custom Express server example only when the integrated server and router don't meet a specific requirement.
API routes are useful HTTP boundaries, not durable job workers or storage. Some hosts execute handlers as short-lived functions with timeouts and nonpersistent local filesystems. Put durable state in a database and long-running work behind a queue and worker.
Common Mistakes
- Mixing router conventions: use
page.jsandroute.jsunderapp/, not Pages Router handler signatures. - Making everything a Client Component: keep the client boundary around interactive code instead of automatically moving the whole application into it.
- Treating a working development server as a deployment test: run linting and a production build before shipping.
- Ignoring infrastructure: when self-hosting, review reverse-proxy protection, streaming support, and cache coordination before scaling to multiple instances.
FAQ
How do I create a new Next.js app?
Run npx create-next-app@16.4.2 your-app --use-npm to reproduce this starter, then choose the documented JavaScript and App Router options. Enter the project directory and run npm run dev.
What is the difference between the App Router and Pages Router?
The App Router uses files such as app/about/page.js and shared layout.js files, with Server Components by default. The Pages Router uses files such as pages/about.js; it remains supported, but the App Router is the recommended starting point for new projects.
How do I create a dynamic route in Next.js?
In the App Router, create a bracketed folder such as app/books/[id]/page.js. Await the page's params prop to read id, validate it, and handle missing resources with notFound().
Does Next.js still support pages/api routes?
Yes. A file such as pages/api/books.js remains a supported API route, including in a project that also has App Router pages. For an App Router endpoint, use app/api/books/route.js instead, without defining the same URL twice.
What command starts a Next.js app in production?
For the standard Next.js server, run npm run build successfully, then npm run start. With this starter's scripts, those commands invoke next build and next start; the default server port is 3000.
Further Reading
- Next.js installation — prerequisites, scaffolding, and project commands.
- Layouts and pages — App Router page and layout conventions.
- Dynamic route segments — asynchronous parameters and caching-related requirements.
- Pages Router documentation — the supported alternative used by existing applications.
- Pages Router API routes — request helpers, methods, and response helpers.
- App Router Route Handlers — HTTP endpoints using Web APIs.
- Image optimization — public assets and next/image.
- Deploying Next.js — servers, containers, static exports, and platform support.
