Learn / Frameworks / Next.js / SEO, Images and Deployment

Intermediate 14 min

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.com

Variables 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.
Measure

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} />