SEO, Images and Deployment
Metadata, next/image, environment variables and deploying.
What you will learn
- Set metadata
- Optimize images
- Deploy to Vercel or Docker
A good site is discoverable, fast and live on the internet. Next.js includes tools for all three.
Metadata for SEO
Export a metadata object (or an async generateMetadata for dynamic pages). Next.js renders the right <title> and meta tags into the HTML that search engines read.
// static
export const metadata = {
title: "Pricing | Acme",
description: "Simple pricing for teams of every size.",
openGraph: { title: "Pricing | Acme", images: ["/og.png"] },
};
// dynamic
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const post = await getPost(slug);
return { title: post.title, description: post.excerpt };
}Also add app/sitemap.ts and app/robots.ts to generate sitemap.xml and robots.txt.
Images
next/image resizes and compresses images, serves modern formats, lazy-loads them and reserves space to avoid layout shift. Always give it dimensions (or fill).
import Image from "next/image";
<Image src="/hero.jpg" alt="Team at work" width={1200} height={600} priority />Use priority for the main above-the-fold image only. For remote images, allow the host in next.config.ts under images.remotePatterns.
import { Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"] });
// <body className={inter.className}>Fonts are downloaded at build time and self-hosted, avoiding extra requests and layout jumps.
# .env.local (never commit)
DATABASE_URL=postgres://...
NEXT_PUBLIC_SITE_URL=https://example.comVariables are server-only by default. Only names starting with NEXT_PUBLIC_ are exposed to the browser, so never put secrets in them.
npm run build # compile and show which routes are static or dynamic
npm start # run the production server- Vercel: push to GitHub, import the repo, done. The easiest option.
- Docker: set
output: "standalone"in the config and copy the standalone build into a small image. - Static export:
output: "export"produces plain files for any static host, if you use no server features.
Run Lighthouse in Chrome DevTools and check Core Web Vitals (LCP, CLS, INP). They influence both user experience and search ranking.
Try it yourself
Add unique metadata to a blog post page using generateMetadata, and switch a plain <img> to next/image.
Show solution
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
return { title: `Post: ${slug}`, description: `Read ${slug} on our blog.` };
}
// <Image src="/cover.jpg" alt="Cover" width={800} height={400} />