Handbooks / Next.js / Chapter 9
Optimization and Deployment
7 pages · ~5 min✓ Reviewed
Builds on Testing and Monitoring.
Optimized Assets: next/image and next/font
1. next/image Basics
<Image>resizes, converts to WebP/AVIF, lazy-loads- Reserves space, so no layout shift (CLS)
- Needs
src+width+height, orfillin aposition: relativeparent - Static import gives width, height,
blurDataURLfree
2. Loading and Remote Hosts
| Image | Use |
|---|---|
| Above-fold LCP | priority (preload) |
| Everything else | lazy by default |
Responsive or fill | sizes avoids viewport-wide file |
typescript
images: {
remotePatterns: [{
protocol: 'https',
hostname: 'cdn.example.com',
pathname: '/img/**',
}],
}in next.config; unlisted host errors at request time
3. next/font
typescript
import { Inter } from 'next/font/google' const inter = Inter({ display: 'swap' }) // <body className={inter.className}>
- Sources:
next/font/googleornext/font/local - Build-time download, self-hosted, no Google requests
- Apply
classNameorvariableon<html>/<body> - When must LCP get priority? Always for the hero image
- Why remotePatterns? Blocks unknown hosts
Metadata API and SEO
1. Defining Metadata
- Export static metadata or async generateMetadata({ params })
- Only in
layout.tsxorpage.tsx, Server Components only - Child segments override and merge with parent metadata
| Field | Value |
|---|---|
title | string or { template: '%s | Site', default } |
description | plain string |
openGraph, twitter | share card data |
alternates.canonical | canonical URL |
robots | index / follow rules |
generateMetadatafetches data for dynamic pages- Same
fetchas the page is deduplicated: no double request
Environment Variables and Bundle Analysis
1. Load order and files
- process.envwins
- .env.localnot in test
- .env.development / .production.env.test
- .envdefaults
- .env.local holds secrets: keep it out of git
- Real
process.envbeats every file
2. Client vs server
| Name | Where it lives | In browser |
|---|---|---|
NEXT_PUBLIC_API | inlined at build | value |
DB_URL | server only | undefined |
Output Modes: Default, Standalone and Static Export
1. Default and Standalone
- Default:
next buildthennext startruns a Node server - Supports SSR, ISR, route handlers, middleware, image optimization
output: 'standalone'traces needed files into .next/standalone- Gets a minimal
server.jsand trimmednode_modules
bash
# next.config: output: 'standalone'
cp -r public .next/standalone/public
cp -r .next/static .next/standalone/.next/static
node .next/standalone/server.jscopy the assets, then run
Standalone skips assets
It does not copy public/ or .next/static. Copy them in, or serve from a CDN, or assets return 404.
2. Static Export
output: 'export'emits plain HTML/CSS/JS to out/- No server: host on any static host or CDN
- Dynamic routes need
generateStaticParams
Deploying to Vercel or a Node Server
1. Vercel
- Connect the Git repo: every push builds automatically
- Preview per branch/PR, Production on the main branch
- Env vars set per environment: Production, Preview, Development
- Routes → serverless/edge functions, static → CDN, ISR and images managed with zero config
2. Self-Hosted Node and Docker
- next build
- next start -p 3000or node .next/standalone/server.js
- PM2 / systemdsupervise
- nginx / LBTLS + gzip
- Docker multi-stage: deps → build → runner using the standalone output
dockerfile
FROM node:22-alpine AS runner USER nextjs ENV PORT=3000 HOSTNAME=0.0.0.0 EXPOSE 3000 CMD ["node", "server.js"]
non-root user, bind to all interfaces
3. Self-Hosting Tradeoffs
| You own |
|---|
Production Cheat Sheet
1. Image and Font
| Must know | |
|---|---|
next/image | width+height or fill; priority for LCP; sizes if responsive; remotePatterns for remote hosts |
next/font | google or local; build-time, self-hosted; className on <body> |
2. Metadata and SEO
- Export
metadataor asyncgenerateMetadata title.templatesets the title pattern, e.g.%s | Siteapp/sitemap.tsandapp/robots.tsare served automatically- JSON-LD: a
<script type="application/ld+json">tag in the page
3. Env and Analysis
NEXT_PUBLIC_is build-time and client-visibleANALYZE=true next buildwith@next/bundle-analyzer
Check yourself
You add <Image src="https://cdn.shop.com/a.jpg" width={400} height={300} alt="" /> and it throws at request time. What's missing?
- The host isn't allow-listed in
remotePatternsin next.config. - Add protocol, hostname and pathname for cdn.shop.com.
You set NEXT_PUBLIC_API_URL in the host dashboard and restart the running server. The browser still calls the old URL. Why?
NEXT_PUBLIC_values are inlined into the client bundle at build time.- Only a rebuild picks up the new value.
Your standalone Docker container runs node server.js, but CSS, JS and images return 404. Fix?
- Standalone doesn't copy
public/or.next/static. - Copy both into the standalone folder, or serve them from a CDN.