SEO Files

Back

Loading concept...

🗺️ SEO Files in Next.js: Your Website’s Tour Guides

The Story of Lost Websites

Imagine the internet is a massive library with billions of books. Google is the helpful librarian who tells visitors where to find what they’re looking for.

But here’s the problem: How does the librarian know what’s in YOUR book?

That’s where SEO files come in! They’re like special notes you leave for the librarian, saying:

  • “Here’s a pretty picture of my book cover!” (ImageResponse API)
  • “Don’t look at my private diary pages!” (robots.ts)
  • “Here’s a complete map of my book!” (sitemap.ts)
  • “My book is SO big, here are multiple maps!” (generateSitemaps)

Let’s meet each of these helpers!


🖼️ ImageResponse API: Creating Beautiful Preview Pictures

What Is It?

When you share a link on Twitter, LinkedIn, or WhatsApp, you see a preview image. That’s called an Open Graph image or OG image.

Think of it like this: Before someone visits your house, they see a photo of it. A beautiful photo makes them want to come inside!

The Magic Behind It

The ImageResponse API lets you create images using code. No Photoshop needed!

// app/about/opengraph-image.tsx
import { ImageResponse } from 'next/og'

export const runtime = 'edge'
export const alt = 'About My Company'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'

export default function Image() {
  return new ImageResponse(
    (
      <div style={{
        fontSize: 60,
        background: 'linear-gradient(to right, #4F46E5, #7C3AED)',
        color: 'white',
        width: '100%',
        height: '100%',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
      }}>
        Welcome to My Website! 🚀
      </div>
    ),
    { ...size }
  )
}

How It Works

graph TD A["Someone shares your link"] --> B["Social media asks for image"] B --> C["Next.js runs your code"] C --> D["ImageResponse creates image"] D --> E["Beautiful preview appears!"]

Key Points

Property What It Does
runtime: 'edge' Makes it super fast
alt Describes image for blind users
size Width and height in pixels
contentType Usually image/png

Special File Names

  • opengraph-image.tsx → For Facebook, LinkedIn
  • twitter-image.tsx → For Twitter/X
  • icon.tsx → For browser tab icons

Pro tip: You can use the same code for both! Just export from two files.


🤖 robots.ts: Your Website’s Security Guard

What Is It?

Imagine your website is a house. Some rooms are for guests (public pages). Some rooms are private (admin panel, checkout, etc.).

The robots.ts file is like a security guard who tells search engines:

  • “Yes, you can look at this room!”
  • “No, stay away from that room!”

The Simple Version

// app/robots.ts
import { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
      disallow: '/private/',
    },
    sitemap: 'https://mysite.com/sitemap.xml',
  }
}

Understanding Each Part

Part Meaning
userAgent: '*' All search engines
allow: '/' They CAN visit everything
disallow: '/private/' But NOT the /private folder
sitemap “Here’s my map, use it!”

Multiple Rules Example

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

What Gets Generated

Your code becomes a text file that looks like:

User-Agent: Googlebot
Allow: /
Disallow: /admin/

User-Agent: Bingbot
Allow: /
Disallow: /admin/
Disallow: /api/

Sitemap: https://mysite.com/sitemap.xml

🗺️ sitemap.ts: Your Website’s Complete Map

What Is It?

A sitemap is like a table of contents for your website. It tells Google:

  • “Here are ALL my pages!”
  • “This page was updated yesterday!”
  • “This page is REALLY important!”

Basic Sitemap

// app/sitemap.ts
import { MetadataRoute } from 'next'

export default function sitemap(): MetadataRoute.Sitemap {
  return [
    {
      url: 'https://mysite.com',
      lastModified: new Date(),
      changeFrequency: 'yearly',
      priority: 1,
    },
    {
      url: 'https://mysite.com/about',
      lastModified: new Date(),
      changeFrequency: 'monthly',
      priority: 0.8,
    },
    {
      url: 'https://mysite.com/blog',
      lastModified: new Date(),
      changeFrequency: 'weekly',
      priority: 0.5,
    },
  ]
}

Understanding the Properties

graph TD A["url"] --> B["Where is the page?"] C["lastModified"] --> D["When was it updated?"] E["changeFrequency"] --> F["How often does it change?"] G["priority"] --> H["How important is it? 0-1"]
Property Values Meaning
url Full URL The page address
lastModified Date When you last edited it
changeFrequency always, hourly, daily, weekly, monthly, yearly, never How often it changes
priority 0.0 to 1.0 1 = Most important

Dynamic Sitemap from Database

// app/sitemap.ts
import { MetadataRoute } from 'next'

export default async function sitemap(): MetadataRoute.Sitemap {
  // Get all blog posts from database
  const posts = await fetch('https://api.mysite.com/posts')
    .then(res => res.json())

  const blogUrls = posts.map((post) => ({
    url: `https://mysite.com/blog/${post.slug}`,
    lastModified: post.updatedAt,
  }))

  return [
    {
      url: 'https://mysite.com',
      lastModified: new Date(),
    },
    ...blogUrls,
  ]
}

📚 generateSitemaps: When One Map Isn’t Enough

The Problem

Imagine you have a bookstore with 100,000 books. One map would be HUGE and slow!

Google says: “Please keep sitemaps under 50,000 URLs!”

The Solution: Multiple Sitemaps

The generateSitemaps function lets you split your sitemap into smaller pieces.

// app/products/sitemap.ts
import { MetadataRoute } from 'next'

export async function generateSitemaps() {
  // Count total products
  const totalProducts = await getProductCount()
  const productsPerSitemap = 50000

  // How many sitemaps do we need?
  const numberOfSitemaps = Math.ceil(
    totalProducts / productsPerSitemap
  )

  // Return array of sitemap IDs
  return Array.from(
    { length: numberOfSitemaps },
    (_, i) => ({ id: i })
  )
}

export default async function sitemap({
  id,
}: {
  id: number
}): Promise<MetadataRoute.Sitemap> {
  const start = id * 50000
  const end = start + 50000

  const products = await getProducts(start, end)

  return products.map((product) => ({
    url: `https://mysite.com/products/${product.id}`,
    lastModified: product.updatedAt,
  }))
}

How It Works

graph TD A["You have 120,000 products"] --> B["generateSitemaps runs"] B --> C["Returns 3 sitemap IDs: 0, 1, 2"] C --> D["/products/sitemap/0.xml&lt;br&gt;50,000 products"] C --> E["/products/sitemap/1.xml&lt;br&gt;50,000 products"] C --> F["/products/sitemap/2.xml&lt;br&gt;20,000 products"]

The URLs Created

Sitemap ID URL Contains
0 /products/sitemap/0.xml Products 1-50,000
1 /products/sitemap/1.xml Products 50,001-100,000
2 /products/sitemap/2.xml Products 100,001-120,000

Complete Example

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

const POSTS_PER_SITEMAP = 1000

export async function generateSitemaps() {
  const count = await getPostCount()
  const pages = Math.ceil(count / POSTS_PER_SITEMAP)

  return Array.from({ length: pages }, (_, i) => ({
    id: i,
  }))
}

export default async function sitemap({
  id,
}: {
  id: number
}): Promise<MetadataRoute.Sitemap> {
  const offset = id * POSTS_PER_SITEMAP
  const posts = await getAllPosts(offset, POSTS_PER_SITEMAP)

  return posts.map((post) => ({
    url: `https://myblog.com/post/${post.slug}`,
    lastModified: new Date(post.updatedAt),
    changeFrequency: 'weekly',
    priority: 0.7,
  }))
}

🎯 Putting It All Together

Here’s how all four SEO files work as a team:

graph TD A["Your Next.js App"] --> B["robots.ts"] A --> C["sitemap.ts"] A --> D["opengraph-image.tsx"] B --> E["Tells Google what to index"] C --> F["Shows Google all your pages"] D --> G["Makes sharing look beautiful"] C --> H{Too many pages?} H -->|Yes| I["Use generateSitemaps"] H -->|No| J["Single sitemap is fine"]

Quick Reference

File Purpose Example Location
robots.ts Control search engines app/robots.ts
sitemap.ts List all pages app/sitemap.ts
opengraph-image.tsx Social preview images app/opengraph-image.tsx
generateSitemaps Split large sitemaps Inside sitemap.ts

🚀 You Did It!

You now understand the four magical SEO files in Next.js:

  1. ImageResponse API - Creates beautiful preview images with code
  2. robots.ts - Tells search engines what they can and can’t see
  3. sitemap.ts - Gives search engines a map of your website
  4. generateSitemaps - Splits big maps into smaller pieces

These helpers work together to make sure Google (and your users) can find, preview, and explore your website easily.

Remember: A well-mapped website is a well-visited website! 🗺️✨

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.