SEO for Developers: Metadata, Sitemaps and Structured Data in Next.js

September 24, 2026

SEO for Developers: Metadata, Sitemaps and Structured Data in Next.js
SEO12 min read

Most developers treat SEO as someone else's job, something marketing handles with spreadsheets and keyword tools. But a large part of SEO is plain engineering: how your pages are rendered, what your HTML says, how fast it loads, and whether search engines can crawl it. Those are things only the developer can fix.

This guide covers the technical SEO setup for a Next.js App Router project. It will not promise you first place on Google, since rankings depend on content and competition too, but it will make sure the technical side is never the thing holding you back.

What Search Engines Need From You

GoalYour job as a developer
Be crawlableReal HTML, working links, robots.txt, sitemap
Be understandableGood titles, descriptions, headings, structured data
Be fastGood Core Web Vitals, optimized images and fonts
Be trustworthyHTTPS, no broken pages, clean redirects, canonical URLs
Be shareableOpen Graph and Twitter cards for link previews

Rendering Matters

Search engines can run JavaScript, but content that is in the HTML on first load is indexed more reliably and faster. With the App Router, server components render HTML on the server by default, which is a big SEO advantage over a client-only single-page app.

  • Keep your main content in server components.
  • Avoid hiding important text behind client-only fetches after load.
  • Use real <a href> links, not click handlers on divs, so crawlers can follow them.
  • Use static generation or caching for pages that rarely change.

Metadata: Titles and Descriptions

The title and description are the first things a person sees in search results. In Next.js you define them with the Metadata API. A global default goes in the root layout:

// app/layout.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  metadataBase: new URL('https://yourdomain.com'),
  title: {
    default: 'Your Name | Full-Stack Developer',
    template: '%s | Your Name',
  },
  description: 'Full-stack developer building fast web apps with Next.js.',
  openGraph: {
    type: 'website',
    siteName: 'Your Name',
  },
}

For dynamic pages like blog posts, use generateMetadata so each page gets its own title, description, and image:

// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }) {
  const { slug } = await params
  const post = await getPost(slug)

  return {
    title: post.title,
    description: post.excerpt,
    alternates: { canonical: `/blog/${post.slug}` },
    openGraph: {
      title: post.title,
      description: post.excerpt,
      type: 'article',
      publishedTime: post.date,
      images: [post.coverImage],
    },
  }
}

Writing Good Titles and Descriptions

  • Keep titles around 50 to 60 characters so they are not cut off.
  • Keep descriptions around 140 to 160 characters.
  • Put the main topic near the start of the title.
  • Make every page unique. Duplicate titles confuse search engines and users.
  • Write the description for a human. It is an advert for the click.

Canonical URLs

The same page can be reachable at several addresses: with and without a trailing slash, with tracking parameters, or on both www and the bare domain. A canonical tag tells search engines which one is the real version. Set alternates.canonical on every indexable page and redirect the duplicates to it.

Sitemap and Robots

The App Router can generate both files from code. A sitemap lists the pages you want indexed:

// app/sitemap.ts
import type { MetadataRoute } from 'next'
import { getAllPosts } from '@/lib/posts'

export default function sitemap(): MetadataRoute.Sitemap {
  const base = 'https://yourdomain.com'
  const posts = getAllPosts().map((post) => ({
    url: `${base}/blog/${post.slug}`,
    lastModified: new Date(post.date),
  }))

  return [
    { url: base, lastModified: new Date() },
    { url: `${base}/blog`, lastModified: new Date() },
    ...posts,
  ]
}
// app/robots.ts
import type { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: { userAgent: '*', allow: '/', disallow: ['/api/', '/admin/'] },
    sitemap: 'https://yourdomain.com/sitemap.xml',
  }
}

After deploying, submit the sitemap in Google Search Console and check the coverage report for pages that are excluded or erroring.

Structured Data (JSON-LD)

Structured data gives search engines an explicit description of your content, which can unlock rich results. For a blog post, use the BlogPosting type:

const jsonLd = {
  '@context': 'https://schema.org',
  '@type': 'BlogPosting',
  headline: post.title,
  description: post.excerpt,
  image: `https://yourdomain.com${post.coverImage}`,
  datePublished: post.date,
  author: { '@type': 'Person', name: 'Your Name' },
}

return (
  <script
    type="application/ld+json"
    dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
  />
)
Schema typeUse it for
PersonPortfolio or about page
WebSiteYour home page
BlogPostingArticles
BreadcrumbListNavigation trails
FAQPageQuestion and answer sections
ProductE-commerce items

Always validate your markup with Google's Rich Results Test before shipping.

Social Preview Cards

When someone shares your link on WhatsApp, LinkedIn, or X, the preview image and title come from Open Graph tags. A missing image makes your link look unfinished. Use a 1200 by 630 pixel image, and in Next.js you can generate one per page with an opengraph-image file or point to your cover image in the metadata.

Core Web Vitals

Page experience is part of how search engines judge pages, and it matters even more for real users. The three metrics to watch:

MetricWhat it measuresGood targetCommon fix
LCPHow fast the main content appearsUnder 2.5 secondsOptimize and prioritize the hero image
INPHow quickly the page responds to inputUnder 200 msReduce heavy JavaScript on interaction
CLSHow much the layout jumpsUnder 0.1Set image sizes, reserve space, use font fallbacks

Quick Wins in Next.js

  • Use next/image with proper sizes, and add priority to the main above-the-fold image.
  • Use next/font to self-host fonts and avoid layout shift.
  • Load third-party scripts with next/script and the right strategy.
  • Lazy-load heavy components that are not visible on first load.
  • Keep animations on transform and opacity so they stay smooth.

Page Structure and Content

  • Use exactly one h1 per page that describes the topic.
  • Use h2 and h3 in order. Do not skip levels for styling.
  • Write descriptive alt text for meaningful images and empty alt for decorative ones.
  • Use clean, readable URLs like /blog/seo-for-developers.
  • Link related pages to each other with descriptive anchor text.

Common SEO Mistakes

  1. Leaving noindex on after launch from a staging setup.
  2. Blocking the whole site in robots.txt by accident.
  3. Using the same title and description on every page.
  4. Rendering key content only on the client.
  5. Forgetting to redirect old URLs when changing routes.
  6. Huge uncompressed images that wreck LCP.
  7. Never checking Search Console.

Launch Checklist

  1. Unique title and description on every page.
  2. Canonical URLs set, duplicates redirected.
  3. sitemap.xml and robots.txt live and submitted.
  4. JSON-LD on posts and the home page, validated.
  5. Open Graph image on every shareable page.
  6. Lighthouse and real-user Core Web Vitals checked on mobile.
  7. Site added to Google Search Console and Bing Webmaster Tools.

The Verdict

Technical SEO is not magic. It is a checklist that takes an afternoon to implement and pays off for as long as the site exists. Get the foundations right, then spend your real effort on content worth ranking.

Your code decides whether search engines can find, read, and trust your pages. Your writing decides whether anyone clicks. You need both.

All articlesOussama Fannah