DEV Community

ANKUSH CHOUDHARY JOHAL
ANKUSH CHOUDHARY JOHAL

Posted on • Originally published at johal.in

How to Implement SEO with Next.js 15 and Sitemap 4.0 for E-Commerce Sites with 10k Products

How to Implement SEO with Next.js 15 and Sitemap 4.0 for E-Commerce Sites with 10k Products

E-commerce sites with 10,000+ products face unique SEO challenges: scaling metadata, avoiding duplicate content, ensuring fast indexation of new products, and maintaining Core Web Vitals performance. Next.js 15’s App Router, Metadata API, and Partial Prerendering paired with Sitemap 4.0’s dynamic generation capabilities solve these pain points for large-scale stores.

Prerequisites

Before starting, ensure you have:

  • An existing Next.js 15 project (App Router enabled by default)
  • Sitemap 4.0 installed via npm install sitemap@4
  • A product data source (headless CMS, PostgreSQL, MongoDB, etc.) with 10k+ product records
  • Basic knowledge of Next.js App Router and SEO fundamentals

Step 1: Configure Per-Page Metadata with Next.js 15 Metadata API

Next.js 15’s Metadata API lets you define static or dynamic metadata for every page, critical for product pages that need unique titles, descriptions, and canonical URLs. For static pages (home, category landing pages), export a static metadata object:

// app/category/[slug]/page.tsx
export const metadata = {
  title: 'Men’s Running Shoes | Your Store',
  description: 'Shop men’s running shoes with free shipping on orders over $50.',
  openGraph: {
    title: 'Men’s Running Shoes | Your Store',
    description: 'Shop men’s running shoes with free shipping on orders over $50.',
    url: 'https://yourstore.com/category/mens-running-shoes',
  },
};
Enter fullscreen mode Exit fullscreen mode

For dynamic product pages, use the generateMetadata function to fetch product data and generate unique metadata per product:

// app/product/[id]/page.tsx
export async function generateMetadata({ params }: { params: { id: string } }) {
  const product = await fetchProduct(params.id); // Fetch from your data source
  return {
    title: `${product.name} | ${product.category} | Your Store`,
    description: product.shortDescription.slice(0, 155), // Match meta description length
    canonical: `https://yourstore.com/product/${product.slug}`,
    openGraph: {
      images: [product.featuredImage],
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

For 10k products, this ensures every product has unique, crawlable metadata, avoiding duplicate content penalties from generic titles.

Step 2: Generate Dynamic Sitemaps with Sitemap 4.0

Static sitemaps fail for 10k+ products, as they require manual updates and hit URL limits. Sitemap 4.0 supports dynamic, on-demand sitemap generation, and can split sitemaps into chunks (though 10k URLs fit in a single sitemap, splitting by category improves crawl efficiency).

Create a sitemap route in Next.js 15’s App Router:

// app/sitemap.ts
import { SitemapStream, streamToPromise } from 'sitemap';
import { Readable } from 'stream';

export async function GET() {
  const products = await fetchAllProducts(); // Fetch 10k products from your data source
  const sitemapStream = new SitemapStream({ hostname: 'https://yourstore.com' });

  // Add static pages first
  sitemapStream.write({ url: '/', changefreq: 'daily', priority: 1.0 });
  sitemapStream.write({ url: '/cart', changefreq: 'never', priority: 0.0, lastmod: new Date().toISOString() });

  // Add all product pages
  products.forEach((product) => {
    sitemapStream.write({
      url: `/product/${product.slug}`,
      changefreq: 'weekly',
      priority: 0.8,
      lastmod: product.updatedAt.toISOString(),
    });
  });

  sitemapStream.end();
  const sitemap = await streamToPromise(Readable.from(sitemapStream));

  return new Response(sitemap, {
    headers: { 'Content-Type': 'application/xml' },
  });
}
Enter fullscreen mode Exit fullscreen mode

Sitemap 4.0 automatically handles XML formatting, and Next.js 15 caches the sitemap response by default. For stores with more than 50k products, split sitemaps using Sitemap 4.0’s chunking feature:

const smStream = new SitemapStream({ hostname: 'https://yourstore.com', chunkSize: 5000 }); // Split into 2 chunks for 10k products
Enter fullscreen mode Exit fullscreen mode

Exclude out-of-stock or discontinued products from the sitemap by filtering them out during the product fetch step to avoid wasting crawl budget.

Step 3: Optimize Rendering for SEO with Next.js 15

Next.js 15’s Partial Prerendering (PPR) and Incremental Static Regeneration (ISR) ensure product pages are fast for users and crawlable for search engines. For products with infrequent updates, use ISR with a revalidation time:

// app/product/[id]/page.tsx
export const revalidate = 3600; // Revalidate every hour

export default async function ProductPage({ params }: { params: { id: string } }) {
  const product = await fetchProduct(params.id);
  return (

      {product.name}
      {product.description}

  );
}
Enter fullscreen mode Exit fullscreen mode

For real-time stock updates, use On-Demand Revalidation to update the product page immediately when stock changes, ensuring search engines see the latest data. Avoid client-side only rendering for product content, as search engine crawlers may not execute JavaScript to index that content.

Step 4: Fix Duplicate Content and URL Structure

E-commerce sites often have duplicate content from filtered URLs (e.g., /category/shoes?size=10&color=red) and pagination. Use canonical URLs to point filtered pages to the base category page:

// app/category/[slug]/page.tsx
export async function generateMetadata({ searchParams }: { searchParams: { size?: string; color?: string } }) {
  const canonicalUrl = `https://yourstore.com/category/${params.slug}`;
  return {
    canonical: searchParams.size || searchParams.color ? canonicalUrl : undefined,
  };
}
Enter fullscreen mode Exit fullscreen mode

Block low-value URLs (cart, checkout, user account) via robots.txt:

// public/robots.txt
User-agent: *
Disallow: /cart
Disallow: /checkout
Disallow: /account
Disallow: /search?*
Enter fullscreen mode Exit fullscreen mode

Step 5: Optimize Core Web Vitals for Large Product Catalogs

Next.js 15’s built-in optimizations reduce manual work for Core Web Vitals:

  • Use the next/image component for product images to auto-optimize size, format (WebP/AVIF), and lazy load offscreen images.
  • Use next/font to self-host fonts and avoid third-party render-blocking requests.
  • Split product grids into chunks with Suspense to avoid large initial payloads for category pages with 100+ products.

For infinite scroll product listings, use the History API to update URLs as users scroll, ensuring crawlers can discover all products via the sitemap even if the scroll is client-side.

Step 6: Validate and Monitor SEO Performance

After implementation:

  • Submit your sitemap to Google Search Console and Bing Webmaster Tools to trigger indexation.
  • Use the Sitemap 4.0 validator to check for XML errors in your sitemap.
  • Run Lighthouse audits on product pages to check Core Web Vitals and metadata correctness.
  • Monitor indexation coverage in Search Console to identify and fix crawl errors for product pages.

Conclusion

Combining Next.js 15’s Metadata API, Partial Prerendering, and Sitemap 4.0’s dynamic generation lets you scale SEO for 10k+ product e-commerce sites without manual overhead. This setup ensures all products are indexed quickly, metadata is unique, and performance meets search engine standards, driving more organic traffic to your store.

Top comments (0)