> Dynamic XML Sitemap Next.js

Generating an XML sitemap for your dynamic Next.Js website.

Recently I developed a dynamic website for a client in Next.Js utilizing GraphCMS for content management.

I needed to create an XML Sitemap to improve SEO and allow search engines like Google to detect new content and crawl the website more efficiently, improving ranking. Sitemaps are the easiest way to communicate with Google. They indicate the URLs that belong to your website and when they update so that Google can easily detect new content and crawl your website more efficiently.

Creating Sitemaps are easy enough for a static website with not much content, but what if you have dynamically generated pages based on the content in your CMS? This is where we can leverage getServerSideProps, a feature in Next.Js to generate an XML sitemap on-demand based on data from queries to your CMS.

References

Code Example:

The web application queries the instance of GraphCMS where I host and manage all of the websites content and products.

Some products have a one to many relationship to various categories, while products like accessories have a one to one relationship.

To achieve dynamically generating the XML Sitemap on-demand, follow the code example below: NOTE: Make sure you have your environment variables setup correctly ref and in Vercel.

// root/pages/sitemap.xml.tsx
import { GraphQLClient, gql } from "graphql-request";
import { IProducts, ICategories, IServices } from "../types";

function generateSiteMap({
  accessories,
  products,
  categories,
  services,
}: {
  accessories: IProducts;
  products: IProducts;
  categories: ICategories;
  services: IServices;
}) {
  const baseUrl = `${
    process.env.NODE_ENV === "development"
      ? process.env.DEV_URL
      : process.env.PROD_URL
  }`;

  // Retrieves all products with their associated category
  // NOTE: Some products can be associated with multiple categories (one to many)
  const allProducts = [...accessories, ...products];
  const allProductUrls = [];

  allProducts.forEach((e) => {
    if (e.category) {
      allProductUrls.push(`${baseUrl}/products/${e.category.slug}/${e.slug}`);
    } else {
      e.categories.forEach(({ slug }) =>
        allProductUrls.push(`${baseUrl}/products/${slug}/${e.slug}`)
      );
    }
  });

  return `<?xml version="1.0" encoding="UTF-8"?>
   <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     <!--We manually set the two URLs we know already-->
     <url>
       <loc>${baseUrl}/</loc>
     </url>
     <url>
      <loc>${baseUrl}/about-us</loc>
     </url>
     <url>
       <loc>${baseUrl}/product</loc>
     </url>
     <url>
       <loc>${baseUrl}/products</loc>
     </url>
     <url>
       <loc>${baseUrl}/services</loc>
     </url>
     <url>
       <loc>${baseUrl}/contact</loc>
     </url>
     ${services
       .map(({ slug }) => {
         return `
       <url>
           <loc>${`${baseUrl}/services#${slug}`}</loc>
       </url>
     `;
       })
       .join("")}
     ${categories
       .map(({ slug }) => {
         return `
       <url>
           <loc>${`${baseUrl}/products/${slug}`}</loc>
       </url>
     `;
       })
       .join("")}
    ${accessories
      .map(({ slug }) => {
        return `
      <url>
          <loc>${`${baseUrl}/product/${slug}`}</loc>
      </url>
    `;
      })
      .join("")}
    ${products
      .map(({ slug }) => {
        return `
      <url>
          <loc>${`${baseUrl}/product/${slug}`}</loc>
      </url>
    `;
      })
      .join("")}
    ${allProductUrls
      .map((url) => {
        return `
      <url>
          <loc>${url}</loc>
      </url>
    `;
      })
      .join("")}
   </urlset>
 `;
}

function SiteMap() {
  // getServerSideProps will do the heavy lifting
}

export async function getServerSideProps({ res }) {
  const client = new GraphQLClient(process.env.GRAPHCMS_PROJECT_API);

  const query = gql`
    query GetAllSlugs {
      categories {
        slug
        products {
          ... on Product {
            slug
          }
        }
      }
      category(where: { slug: "wheelchair-accessories" }) {
        slug
        accessories {
          slug
        }
      }
      categories {
        slug
      }
      accessories {
        slug
        category {
          slug
        }
      }
      products {
        slug
        categories {
          slug
        }
      }
      services {
        slug
      }
    }
  `;

  try {
    const data: {
      accessories: IProducts;
      products: IProducts;
      categories: ICategories;
      services: IServices;
    } = await client.request(query);

    const { accessories, products, categories, services } = data;

    // We generate the XML sitemap with the posts data
    const sitemap: string = generateSiteMap({
      accessories,
      products,
      categories,
      services,
    });
    res.setHeader("Content-Type", "text/xml");
    // we send the XML to the browser
    res.write(sitemap);
    res.end();
  } catch (error) {
    console.error(error.message);
    res.end();
  }

  return {
    props: {},
  };
}

export default SiteMap;

XML Sitemap Output:

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<!-- We manually set the two URLs we know already -->
<url>
<loc>https://amemobility.vercel.app/</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/about-us</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/services</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/contact</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/services#custom-wheelchairs</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/services#on-site-service</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/manual-wheelchairs</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/wheelchair-accessories</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/bathroom-aids</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/mobility-scooters</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/mobility-equipment</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/access-ramps</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/walking-aids</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/postural-seating</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/paediatric-mobility</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/heavy-duty-wheelchairs-and-mobility-equipment</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/alber-e-fix-e35</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/alber-e-pilot</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/ki-mobility-rogue-xp</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/magic-mobility-extreme-x8-44-power-chair</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/pride-go-go-lx-with-cts-suspension</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/pride-pathrider-10</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/magic-mobility-frontier-v4-rear-wheel-drive</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/magic-mobility-v4-front-wheel-drive</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/magic-mobility-v6-all-terrain-mid-wheel-drive</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/magic-mobility-v6-compact-40</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/magic-mobility-frontier-v6-compact-73</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/magic-mobility-frontier-v6-hybrid</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/magic-mobility-360-off-road-compact</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/magic-mobility-360-urban-compact</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/magic-mobility-360-crossover-compact</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/permobil-f3-corpus</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/permobil-f5-corpus</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/permobil-f5-corpus-vs</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/permobil-k300-ps-jr</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/permobil-koala-r-net</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/permobil-explorer-mini</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/permobil-m1</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/permobil-m3-corpus</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/permobil-m5-corpus</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/permobil-m300-corpus-hd</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quantum-rehab-r44</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quantum-4frtont</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quantum-edge-3</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quantum-edge-30-stretto</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quantum-q6-edge-20-ilevel</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quantum-edge-z</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quickie-400-m</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quickie-q400-f</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quickie-400-r</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quickie-500-m-sedeo-lite</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quickie-500-m</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quickie-q500-f</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quickie-500-r</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/product/quickie-q700-r-sedeo-pro</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/wheelchair-accessories/alber-e-fix-e35</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/wheelchair-accessories/alber-e-pilot</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/manual-wheelchairs/ki-mobility-rogue-xp</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/magic-mobility-extreme-x8-44-power-chair</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/mobility-scooters/pride-go-go-lx-with-cts-suspension</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/mobility-scooters/pride-pathrider-10</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/magic-mobility-frontier-v4-rear-wheel-drive</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/magic-mobility-v4-front-wheel-drive</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/magic-mobility-v6-all-terrain-mid-wheel-drive</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/magic-mobility-v6-compact-40</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/magic-mobility-frontier-v6-compact-73</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/magic-mobility-frontier-v6-hybrid</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/magic-mobility-360-off-road-compact</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/magic-mobility-360-urban-compact</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/magic-mobility-360-crossover-compact</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/permobil-f3-corpus</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/permobil-f5-corpus</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/permobil-f5-corpus-vs</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/permobil-k300-ps-jr</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/paediatric-mobility/permobil-k300-ps-jr</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/permobil-koala-r-net</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/paediatric-mobility/permobil-koala-r-net</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/permobil-explorer-mini</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/paediatric-mobility/permobil-explorer-mini</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/permobil-m1</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/permobil-m3-corpus</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/permobil-m5-corpus</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/permobil-m300-corpus-hd</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/heavy-duty-wheelchairs-and-mobility-equipment/permobil-m300-corpus-hd</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quantum-rehab-r44</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quantum-4frtont</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quantum-edge-3</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quantum-edge-30-stretto</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/paediatric-mobility/quantum-edge-30-stretto</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quantum-q6-edge-20-ilevel</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quantum-edge-z</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quickie-400-m</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quickie-q400-f</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quickie-400-r</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quickie-500-m-sedeo-lite</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quickie-500-m</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quickie-q500-f</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quickie-500-r</loc>
</url>
<url>
<loc>https://amemobility.vercel.app/products/power-wheelchairs/quickie-q700-r-sedeo-pro</loc>
</url>
</urlset>