Skip to content
    SeaCode
    Learning tracksCode ArenaWorkshops & EventsInstructors
    Log inSign up
    NNext.js

    Next.js foundations

    • What is Next.js?
    • The App Router: folders are routes
    • Dynamic routes
    • Client-side navigation

    Server, client and data

    • Server and Client Components
    • Fetching data on the server
    • loading, error and not-found
    • Caching and revalidation

    Mutations, APIs and security

    • Server Actions
    • Route Handlers: building an API
    • Proxy (formerly Middleware)
    • Authentication and protecting pages

    Going to production

    • Metadata and SEO
    • Images, fonts and performance
    • Environment variables and deploying to Vercel
    • Project: a course catalogue
    Next.js

    Going to production · Lesson 13 of 16

    Metadata and SEO

    metadata and generateMetadata, share images, sitemap and robots.

    9 minutes2 games

    The title, description and share image are what show up on Google, WhatsApp and LinkedIn. In Next.js you export metadata (static) or generateMetadata (for pages with dynamic data) from any page.tsx or layout.tsx, and Next.js turns them into <head> tags.

    TypeScript
    // app/layout.tsx: defaults for the whole site
    import type { Metadata } from "next"
    
    export const metadata: Metadata = {
      metadataBase: new URL("https://www.seacode.tech"),
      title: { default: "SeaCode", template: "%s · SeaCode" },   // pages fill in %s
      description: "Learn to code for real, in Arabic and English.",
      openGraph: { type: "website", siteName: "SeaCode" },
    }
    
    // app/courses/[slug]/page.tsx: per-course metadata from the database
    export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
      const { slug } = await params
      const course = await getCourse(slug)
      if (!course) return { title: "Course not found" }
      return {
        title: course.title,                                   // "React · SeaCode"
        description: course.summary,
        openGraph: { images: [course.thumbnailUrl] },
        alternates: { canonical: "/courses/" + slug },
      }
    }
    TypeScript
    // app/sitemap.ts -> /sitemap.xml
    import type { MetadataRoute } from "next"
    
    export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
      const courses = await getAllCourses()
      return [
        { url: "https://www.seacode.tech", changeFrequency: "weekly", priority: 1 },
        ...courses.map((c) => ({ url: "https://www.seacode.tech/courses/" + c.slug, lastModified: c.updatedAt })),
      ]
    }
    
    // app/robots.ts -> /robots.txt
    export default function robots(): MetadataRoute.Robots {
      return { rules: { userAgent: "*", allow: "/", disallow: ["/admin", "/api"] }, sitemap: "https://www.seacode.tech/sitemap.xml" }
    }

    generateMetadata loads the course

    <title>React · SeaCode</title> + og:image

    Google and social apps read the head

    Important

    If the page and generateMetadata load the same course, the request happens only once, as long as it's a fetch with the same URL or a function wrapped in cache(). SEO isn't only metadata: server-rendered HTML, ordered headings, images with alt, and speed (Core Web Vitals) all matter.

    Quick question

    The root layout has title.template: "%s · SeaCode" and the course page has title: "SQL". What's the tab title?

    Interview question

    How do you get good SEO for a React app?

    Exercise

    Write a simplified generateMetadata(course) returning { title, description }: the title is the course name, and the description is the first 50 characters of the summary followed by … if it was longer. For a null course return { title: "Course not found" }. Print the results with JSON.stringify.

    Expected output:
    {"title":"SQL","description":"Query real data."}
    {"title":"React","description":"Build interactive interfaces from small components…"}
    {"title":"Course not found"}
    JavaScript
    function generateMetadata(course) {
      return {}
    }
    
    console.log(JSON.stringify(generateMetadata({ title: "SQL", summary: "Query real data." })))
    console.log(JSON.stringify(generateMetadata({ title: "React", summary: "Build interactive interfaces from small components, with state and effects." })))
    console.log(JSON.stringify(generateMetadata(null)))

    Learn more from

    • Next.js docs: Metadata and OG imagesnextjs.org
    • Next.js docs: generateMetadatanextjs.org
    • Google Search Central: SEO starter guidedevelopers.google.com
    • Open Graph protocologp.me

    Played 0 of 2 games

    Sign in to save your progress and earn XP.

    PreviousAuthentication and protecting pagesNextImages, fonts and performance
    SeaCode

    Learn to code for real, online or at our branch in front of Cairo University, Giza.

    Learn

    • All courses
    • Interactive tutorials
    • Interview practice problems
    • Mock interview
    • Learning tracks
    • Code Arena
    • Workshops & Events
    • Knowledge library
    • Team projects

    Your account

    • Log in
    • Sign up
    • Learning plan
    • Certificates
    • Site map

    Contact us

    • WhatsApp
    • 010 9094 1104
    • In front of Cairo University, Giza

    © 2026 SeaCode. All rights reserved.