How to create multilingual sitemaps in React Router v7

Organize sitemaps by language for scale

Problem

Sitemaps help search engines discover and crawl all pages on a site. A multilingual site with hundreds or thousands of pages per language can quickly generate enormous sitemaps. A single file listing every URL for every language becomes unwieldy, may exceed the 50,000 URL or 50MB size limits, and makes maintenance difficult. When you update the structure of one language, you must regenerate and revalidate the entire file. This approach does not scale as your site grows.

Solution

Split sitemaps into a hierarchy with a top-level sitemap index that points to separate, language-specific sitemaps. Each language gets its own sitemap file containing only URLs for that language. The index file acts as a directory, listing the location of each language sitemap. This keeps individual files manageable, lets you update each language independently, and scales well as you add new languages or pages. Search engines crawl the index first, then follow links to each language sitemap.

Steps

1. Create a sitemap index route

Create a resource route that returns an XML response listing all language-specific sitemaps.

import type { Route } from "./+types/sitemap";

const LOCALES = ["en", "es", "fr", "de"];

export function loader({ request }: Route.LoaderArgs) {
const { origin } = new URL(request.url);

const sitemapIndex = `<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${LOCALES.map(locale => `  <sitemap>
  <loc>${origin}/sitemap-${locale}.xml</loc>
</sitemap>`).join("
")}
</sitemapindex>`;

return new Response(sitemapIndex, {
  headers: {
    "Content-Type": "application/xml",
  },
});
}

This route works as an API endpoint by returning an XML response. The index lists one sitemap per language, making it easy to add or remove languages by updating the LOCALES array.

2. Create language-specific sitemap routes

Define a resource route with a dynamic segment to generate individual sitemaps for each language.

import type { Route } from "./+types/sitemap.$locale";

export async function loader({ params }: Route.LoaderArgs) {
const locale = params.locale;
const urls = await getUrlsForLocale(locale);

const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls.map(url => `  <url>
  <loc>${url}</loc>
</url>`).join("
")}
</urlset>`;

return new Response(sitemap, {
  headers: {
    "Content-Type": "application/xml",
  },
});
}

async function getUrlsForLocale(locale: string): Promise<string[]> {
return [];
}

The dynamic segment in the route path is parsed and provided as params.locale. Each language sitemap contains only URLs for that specific locale.

3. Fetch pages for each language

Implement the helper function to retrieve all pages for a given locale from your data source.

async function getUrlsForLocale(locale: string): Promise<string[]> {
  const pages = await db.pages.findMany({
    where: { locale, published: true },
    select: { slug: true },
  });

  return pages.map((page) => `https://example.com/${locale}/${page.slug}`);
}

This function queries your database or content source for published pages in the requested language and constructs full URLs. Adjust the query and URL structure to match your application.

4. Register the sitemap routes

Add the sitemap routes to your routes configuration file.

import { type RouteConfig, route } from "@react-router/dev/routes";

export default [
  route("sitemap.xml", "./routes/sitemap.tsx"),
  route("sitemap-:locale.xml", "./routes/sitemap.$locale.tsx"),
] satisfies RouteConfig;

Each route has a URL pattern to match and a file path to the route module. The index route responds at /sitemap.xml and the language routes respond at /sitemap-en.xml, /sitemap-es.xml, and so on.

5. Add the sitemap index to robots.txt

Point search engines to your sitemap index in the robots.txt file at the root of your public directory.

User-agent: *
Allow: /

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

A sitemap index listing each of your sitemaps allows search engines to discover all language-specific sitemaps from a single entry point. Search engines will crawl the index and follow links to each language sitemap automatically.