|$ curl https://forge-ai.dev/api/markdown?path=docs/nextjs
$cat docs/next.js-—-getting-started.md
updated Recently·50 min read·published

Next.js — Getting Started

Next.jsReactFrameworkBeginner to Advanced🎯Free Tools
What is Next.js?

Next.js is a production-grade React framework built by Vercel. It extends React with server-side rendering, static generation, file-based routing, and a rich set of conventions for building full-stack web applications. Unlike plain React (which is client-only), Next.js renders components on the server by default, sending fully formed HTML to the browser for faster initial loads and better SEO.

Since version 13, Next.js introduced the App Router — a new routing paradigm built on React Server Components (RSC). The App Router provides co-located data fetching, streaming, nested layouts, loading states, error boundaries, and parallel routes. This guide covers everything you need to build production applications with the App Router.

info

This guide covers Next.js with the App Router (recommended for all new projects). If you are migrating from Pages Router, see the migration section at the end.
Installation

The fastest way to start a new Next.js project is with create-next-app. It sets up TypeScript, ESLint, Tailwind CSS, and the App Router by default.

terminal
Bash
1# Interactive setup (recommended)
2npx create-next-app@latest my-app
3
4# With specific options
5npx create-next-app@latest my-app \
6 --typescript \
7 --tailwind \
8 --eslint \
9 --app \
10 --src-dir \
11 --import-alias "@/*"

After installation, the project structure looks like this:

project-structure
TEXT
1my-app/
2├── src/
3│ └── app/
4│ ├── layout.tsx # Root layout (required)
5│ ├── page.tsx # Home page (/)
6│ ├── globals.css # Global styles
7│ └── favicon.ico # Favicon
8├── public/ # Static assets
9├── next.config.ts # Next.js configuration
10├── tailwind.config.ts # Tailwind configuration
11├── tsconfig.json # TypeScript configuration
12├── package.json
13└── .env.local # Environment variables
Project Structure

The App Router uses the app/ directory as its root. Every folder inside app/ becomes a route. Special files like page.tsx, layout.tsx, loading.tsx, error.tsx, and not-found.tsx define the UI for each route.

app-directory
TEXT
1app/
2├── layout.tsx # Root layout — wraps all pages
3├── page.tsx # Home page (/)
4├── loading.tsx # Loading UI for /
5├── error.tsx # Error boundary for /
6├── not-found.tsx # 404 page for /
7├── globals.css # Global styles
8
9├── about/
10│ └── page.tsx # /about
11
12├── blog/
13│ ├── layout.tsx # Shared layout for /blog/*
14│ ├── page.tsx # /blog
15│ ├── loading.tsx # Loading UI for /blog
16│ ├── [slug]/
17│ │ └── page.tsx # /blog/:slug
18│ └── tag/
19│ └── [tag]/
20│ └── page.tsx # /blog/tag/:tag
21
22├── dashboard/
23│ ├── layout.tsx # Dashboard layout
24│ ├── page.tsx # /dashboard
25│ ├── settings/
26│ │ └── page.tsx # /dashboard/settings
27│ └── analytics/
28│ └── page.tsx # /dashboard/analytics
29
30├── api/
31│ └── users/
32│ └── route.ts # /api/users (API route)
33
34└── (marketing)/
35 ├── layout.tsx # Marketing layout group
36 ├── pricing/
37 │ └── page.tsx # /pricing (no URL segment)
38 └── features/
39 └── page.tsx # /features

info

Folder names starting with parentheses like (marketing) are route groups. They organize routes without adding a URL segment. This is useful for shared layouts that should not affect the URL path.
Core Concepts

Next.js is built on several key concepts that differentiate it from plain React. Understanding these concepts is essential for building efficient applications.

React Server Components (RSC)

Components that render on the server. They can directly access databases, file systems, and APIs without creating API endpoints. They send zero JavaScript to the client, reducing bundle size dramatically.

File-Based Routing

Routes are defined by the file system. Each folder becomes a URL segment, and special files (page.tsx, layout.tsx) define the UI.

Nested Layouts

Layouts wrap child routes without remounting. They persist across navigations and allow shared UI to remain interactive while child content changes.

Streaming & Suspense

The server can send HTML progressively. Slow components stream in as they resolve while fast components render immediately, reducing Time to First Byte (TTFB).

Server Actions

Async functions that run on the server and can be called directly from client components. They eliminate the need for manual API routes for form submissions and data mutations.

Your First Page

A page is a React component that renders at a specific URL. Create a page.tsx file inside the app/ directory. By default, pages are React Server Components — they run only on the server.

app/page.tsx
TSX
1// app/page.tsx — Home page (/)
2export default function HomePage() {
3 return (
4 <main>
5 <h1>Welcome to Next.js</h1>
6 <p>This page is rendered on the server.</p>
7 </main>
8 );
9}

To add interactivity (event handlers, state, effects), add the 'use client' directive at the top of the file. This marks the component as a Client Component that runs in the browser.

app/counter/page.tsx
TSX
1"use client";
2
3import { useState } from "react";
4
5export default function CounterPage() {
6 const [count, setCount] = useState(0);
7
8 return (
9 <main>
10 <h1>Counter</h1>
11 <p>Count: {count}</p>
12 <button onClick={() => setCount(count + 1)}>
13 Increment
14 </button>
15 </main>
16 );
17}

info

Keep Client Components at the leaf of your component tree. Fetch data in Server Components and pass it down as props. This minimizes the JavaScript sent to the client while keeping interactive parts of your UI responsive.
Layouts

Layouts are special files that wrap pages. They persist across navigations — when a user moves between pages, the layout does not remount. This is perfect for shared UI like navigation bars, sidebars, and footers.

app/layout.tsx
TSX
1// app/layout.tsx — Root layout (required)
2import type { Metadata } from "next";
3import "./globals.css";
4
5export const metadata: Metadata = {
6 title: "My App",
7 description: "Built with Next.js",
8};
9
10export default function RootLayout({
11 children,
12}: {
13 children: React.ReactNode;
14}) {
15 return (
16 <html lang="en">
17 <body>
18 <nav>
19 <a href="/">Home</a>
20 <a href="/about">About</a>
21 <a href="/blog">Blog</a>
22 </nav>
23 <main>{children}</main>
24 <footer>&copy; 2026 My App</footer>
25 </body>
26 </html>
27 );
28}
app/dashboard/layout.tsx
TSX
1// app/dashboard/layout.tsx — Nested layout
2import { Sidebar } from "@/components/sidebar";
3
4export default function DashboardLayout({
5 children,
6}: {
7 children: React.ReactNode;
8}) {
9 return (
10 <div className="flex">
11 <Sidebar />
12 <div className="flex-1 p-8">{children}</div>
13 </div>
14 );
15}

warning

Root layout must contain <html> and <body> tags. Every other layout should only wrap the children it needs — do not add <html> in nested layouts.
Loading & Error States

Next.js provides special files for handling loading and error states. These integrate with React Suspense and Error Boundaries automatically.

app/loading.tsx
TSX
1// app/loading.tsx — Shown while the page is loading
2export default function Loading() {
3 return (
4 <div className="animate-pulse">
5 <div className="h-8 bg-gray-200 rounded w-1/3 mb-4" />
6 <div className="h-4 bg-gray-200 rounded w-full mb-2" />
7 <div className="h-4 bg-gray-200 rounded w-2/3" />
8 </div>
9 );
10}
app/error.tsx
TSX
1// app/error.tsx — Error boundary for this route segment
2"use client";
3
4import { useEffect } from "react";
5
6export default function Error({
7 error,
8 reset,
9}: {
10 error: Error & { digest?: string };
11 reset: () => void;
12}) {
13 useEffect(() => {
14 console.error(error);
15 }, [error]);
16
17 return (
18 <div className="text-center py-20">
19 <h2>Something went wrong!</h2>
20 <p>{error.message}</p>
21 <button onClick={() => reset()}>Try again</button>
22 </div>
23 );
24}
app/not-found.tsx
TSX
1// app/not-found.tsx — 404 page
2import Link from "next/link";
3
4export default function NotFound() {
5 return (
6 <div className="text-center py-20">
7 <h2>404 — Page Not Found</h2>
8 <p>The page you are looking for does not exist.</p>
9 <Link href="/">Go back home</Link>
10 </div>
11 );
12}

info

loading.tsx automatically wraps the page in a Suspense boundary. It streams the loading UI immediately while the page content resolves on the server. This gives users instant feedback instead of a blank screen.
Static vs Dynamic Rendering

Next.js can render pages at build time (static) or at request time (dynamic). By default, pages without dynamic dependencies are statically generated during the build. Pages that use dynamic functions like cookies(), headers(), or searchParams are automatically rendered on the server at request time.

app/page-examples.tsx
TSX
1// Static page — built at build time
2// No dynamic APIs used, so this is static by default
3export default function AboutPage() {
4 return <h1>About Us</h1>;
5}
6
7// Dynamic page — rendered at request time
8// Using cookies() forces dynamic rendering
9import { cookies } from "next/headers";
10
11export default async function DashboardPage() {
12 const cookieStore = await cookies();
13 const session = cookieStore.get("session");
14
15 if (!session) {
16 return <p>Please log in</p>;
17 }
18
19 return <h1>Welcome, {session.value}</h1>;
20}

You can also explicitly force dynamic rendering with export const dynamic = "force-dynamic" or set revalidation intervals for ISR (Incremental Static Regeneration).

app/blog/page.tsx
TSX
1// Force dynamic rendering for this route
2export const dynamic = "force-dynamic";
3
4// Or set revalidation for ISR
5export const revalidate = 60; // Revalidate every 60 seconds
6
7// Or force static with on-demand revalidation
8export const revalidate = false; // Static, never revalidate automatically

info

Static pages are faster because they are served from a CDN with zero server compute. Only use dynamic rendering when the page depends on request-specific data like cookies, headers, or user-specific database queries.
Metadata & SEO

Next.js provides a comprehensive metadata API for SEO. You can define metadata statically with the metadata export or dynamically with the generateMetadata function.

app/page.tsx
TSX
1// Static metadata
2import type { Metadata } from "next";
3
4export const metadata: Metadata = {
5 title: "My Page Title",
6 description: "Page description for search engines",
7 openGraph: {
8 title: "My Page Title",
9 description: "What shows up on social media",
10 images: ["/og-image.png"],
11 },
12 twitter: {
13 card: "summary_large_image",
14 title: "My Page Title",
15 description: "Twitter card description",
16 },
17};
18
19// Dynamic metadata
20export async function generateMetadata({
21 params,
22}: {
23 params: Promise<{ slug: string }>;
24}): Promise<Metadata> {
25 const { slug } = await params;
26 const post = await getPost(slug);
27
28 return {
29 title: post.title,
30 description: post.excerpt,
31 openGraph: {
32 title: post.title,
33 description: post.excerpt,
34 images: [post.coverImage],
35 },
36 };
37}

best practice

Always use generateMetadata for dynamic pages like blog posts. It runs on the server and can fetch data to build accurate metadata. Search engines rely on this for indexing and social sharing previews.
Routing Features

The App Router supports several advanced routing patterns beyond basic file-based routes.

Dynamic Routes

routing-patterns
TEXT
1app/blog/[slug]/page.tsx → /blog/:slug
2app/shop/[...slug]/page.tsx → /shop/* (catch-all)
3app/shop/[[...slug]]/page.tsx → /shop/* (optional catch-all)
4app/(group)/page.tsx → / (route group, no URL segment)

Parallel Routes

Render multiple pages in the same layout simultaneously using named slots. Useful for dashboards, modals, and split views.

app/dashboard/layout.tsx
TSX
1// app/dashboard/layout.tsx
2export default function Layout({
3 children,
4 analytics,
5 team,
6}: {
7 children: React.ReactNode;
8 analytics: React.ReactNode;
9 team: React.ReactNode;
10}) {
11 return (
12 <div className="grid grid-cols-2 gap-4">
13 <div>{children}</div>
14 <div>{analytics}</div>
15 <div>{team}</div>
16 </div>
17 );
18}

Intercepting Routes

Intercept a route as if the user navigated to it, but keep the current context visible. Use the (...) syntax.

intercepting-routes
TEXT
1app/feed/[id]/page.tsx → /feed/123 (full page)
2app/feed/(..)photo/[id]/page.tsx → intercepts /photo/123 as modal
Middleware

Middleware runs before a request is completed. It can modify the request, rewrite URLs, set cookies, redirect, or return early. Place a middleware.ts file at the root of your project (or in src/).

middleware.ts
TSX
1// middleware.ts (project root)
2import { NextResponse } from "next/server";
3import type { NextRequest } from "next/server";
4
5export function middleware(request: NextRequest) {
6 // Check authentication
7 const token = request.cookies.get("session")?.value;
8
9 if (!token && request.nextUrl.pathname.startsWith("/dashboard")) {
10 return NextResponse.redirect(new URL("/login", request.url));
11 }
12
13 // Add custom headers
14 const response = NextResponse.next();
15 response.headers.set("x-request-id", crypto.randomUUID());
16 return response;
17}
18
19export const config = {
20 matcher: [
21 // Match all paths except static files and images
22 "/((?!_next/static|_next/image|favicon.ico|public/).*)",
23 ],
24};

info

Middleware runs at the edge — on CDN nodes close to the user, not on your origin server. This makes it extremely fast for authentication checks, A/B testing, and geolocation-based redirects.
Environment Variables

Next.js supports environment variables out of the box. Variables prefixed with NEXT_PUBLIC_ are exposed to the browser. All other variables are server-only.

.env.local
Bash
1# .env.local (never committed to git)
2DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
3API_SECRET=sk-secret-key
4
5# Available on both server and client
6NEXT_PUBLIC_API_URL=https://api.example.com
7NEXT_PUBLIC_SITE_NAME=My App
app/server-page.tsx
TSX
1// Server Component — can access all env vars
2export default async function ServerPage() {
3 const dbUrl = process.env.DATABASE_URL; // ✅ Available
4 const apiUrl = process.env.NEXT_PUBLIC_API_URL; // ✅ Available
5
6 return <p>Server-only content</p>;
7}
app/client-page.tsx
TSX
1// Client Component — only NEXT_PUBLIC_ vars
2"use client";
3
4export default function ClientPage() {
5 const apiUrl = process.env.NEXT_PUBLIC_API_URL; // ✅ Available
6 const dbUrl = process.env.DATABASE_URL; // ❌ undefined
7
8 return <p>Client content: {apiUrl}</p>;
9}

warning

Never prefix sensitive values like API keys or database URLs with NEXT_PUBLIC_. Those variables are embedded in the client bundle and visible to anyone who inspects the page source.
Quick Reference
FilePurposeRequired
page.tsxDefines the UI for a routeYes (per route)
layout.tsxShared UI that wraps child routesRoot only
loading.tsxLoading UI (Suspense boundary)No
error.tsxError boundaryNo
not-found.tsx404 UINo
route.tsAPI endpoint (Route Handler)No
template.tsxLike layout but remounts on navigationNo
default.tsxFallback for parallel routesNo

best practice

Next.js with the App Router is the recommended approach for new projects. Server Components are the default, so your application sends less JavaScript to the client by default. Only add 'use client' when you need interactivity.
$Blueprint — Engineering Documentation·Section ID: NEXTJS-01·Revision: 1.0