Get Back
22 April, 2025
SEO Best Practices for Next.js 15 App Router
dev
See Full Picture
https://images.pexels.com/photos/40185/mac-freelancer-macintosh-macbook-40185.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1

SEO Best Practices for Next.js 15 App Router

This guide helps you improve SEO in a Next.js 15 project using the App Router. You'll use built-in features like the metadata API, dynamic routes, and structured data—no CMS required.

1. Use the metadata API

Next.js 15 provides a built-in metadata API for handling SEO metadata.

app/layout.tsx

import type { Metadata } from "next";

export const metadata: Metadata = {
  metadataBase: new URL("https://bmed.vercel.app"),
  title: {
    default: "BounaderMedRafik",
    template: "%s | BounaderMedRafik",
  },
  description: "Explore my portfolio to discover my passions and skills.",
  openGraph: {
    title: "Mohamed Rafik",
    description: "A glimpse into my world.",
    locale: "en_DZ",
    type: "website",
    url: "https://bmed.vercel.app",
    images: [
      {
        url: "https://example.com/og.jpg",
        width: 800,
        height: 600,
      },
    ],
  },
  keywords: ["portfolio", "web dev", "Next.js", "Rafik", "coding"],
};

          

2. Add Metadata for Dynamic Routes

Use generateMetadata inside dynamic routes like /thoughts/[id] to create per-page meta tags.

app/thoughts/[id]/page.tsx

import { thoughts } from "@/db/thoughts";
import type { Metadata } from "next";

export async function generateMetadata({ params }: { params: { id: string } }): Promise<Metadata> {
  const thought = thoughts.find((t) => t.id === params.id);

  if (!thought) return { title: "Not Found" };

  return {
    title: thought.title,
    description: thought.summary || "Read this thought piece.",
    openGraph: {
      title: thought.title,
      description: thought.summary,
      type: "article",
      images: [{ url: thought.image }],
    },
  };
}

          

3. Generate a Sitemap

Use the App Router API to generate a sitemap dynamically.

app/sitemap.ts

import { DOMAIN } from "@/db/data";
import { thoughts } from "@/db/thoughts";

export default async function sitemap() {
  const thoughtUrls = thoughts.map((t) => ({
    url: https://www.yourblogwebsite.com,
    lastModified: t.date,
  }));

  return [
    { url: DOMAIN, lastModified: new Date() },
    ...thoughtUrls,
  ];
}

          

4. Add a Robots.txt File

A robots.txt file tells crawlers which pages they can access.

app/robots.ts

import { MetadataRoute } from "next";
import { DOMAIN } from "@/db/data";

export default function robot(): MetadataRoute.Robots {
  return {
    rules: [
      {
        userAgent: "*",
        allow: "/",
        disallow: [],
      },
    ],
    sitemap: https://www.yourblogwebsite.com,
  };
}

  

5. Add Structured Data (JSON-LD)

Structured data helps search engines understand your content better.

app/thoughts/[id]/page.tsx

<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
    __html: JSON.stringify({
      "@context": "https://schema.org",
      "@type": "BlogPosting",
      headline: thought.title,
      datePublished: thought.date,
      description: thought.summary,
      image: thought.image,
      author: {
        "@type": "Person",
        name: "Mohamed Rafik",
      },
    }),
  }}
/>

          

Summary Table

FeatureStatus
metadata APIImplemented
Per-page metadataRecommended
SitemapImplemented
Robots.txtImplemented
Open Graph metaImplemented
Structured data (JSON-LD)Recommended
Made With Kisses By Mohamed Rafik