Build a Dynamic Sitemap with Angular 17 SSR

A sitemap is essential for SEO as it enhances your site's crawlability by search engines, resulting in better visibility in search results. In this post, I will demonstrate how I implemented a dynamic, server-side-rendered sitemap using Angular 17.

Avatar

Kasperi Pohtinen

3 min read · Jan 17, 2024 · 0 likes

A sitemap.xml plays a pivotal role in optimizing a website for search engines. It is an XML file that lists all the important URLs of a website, serving as a guide for search engines to understand the structure and content of the site. This file is particularly crucial for several reasons:

  • Enhanced Discoverability: Sitemap.xml helps search engines like Google to efficiently locate and crawl web pages, even those that might be hard to find through normal browsing.
  • Improved Indexing: By providing search engines with a structured layout of all pages, a sitemap.xml ensures more thorough and faster indexing, which is critical for new or updated content.
  • SEO Optimization: It aids in SEO by ensuring that search engines can easily access all relevant pages, potentially improving a site's visibility and ranking in search results.

In this post I will explain what you need to do to have server rendered sitemap in your Angular 17 website.

Modify your server.ts

Create a new endpoint in your server.ts file to serve the sitemap. This should include the logic for creating the sitemap. In my experience, I used a separate backend connected to a database to handle sitemap creation, but you can incorporate it directly into server.ts. Asynchronous operations are also feasible.

For an effective sitemap, list all indexable pages. In my case, I included a few static pages by hardcoding them into the logic. Additionally, there are dynamic post pages (similar to the one you are reading now), which are generated individually for each request.

Make sure you have your sitemap endpoint (and other custom endpoints) before Angular's default endpoints.

server.ts

// Sitemap.xml
server.get('/sitemap.xml', async (req, res) => {
  const sitemap = await fetch(`https://api.learnedtoday.app/api/sitemap`);
  const text = await sitemap.text();
  res.setHeader('Content-Type', 'application/xml');
  res.send(text);
});

Backend server

This logic can be implemented directly in server.ts, eliminating the need for a separate server. I am using sitemap package from npm for creating a sitemap.

sitemap.ts deployed on vercel

export default async function handler(
  req: VercelRequest,
  res: VercelResponse,
): Promise<VercelResponse> {
  if (req.method !== 'GET') {
    return res.status(405).json({ code: ApiError.METHOD_NOT_ALLOWED });
  }

  await seed();

  const posts = await db
    .selectFrom('posts')
    .select(['id', 'title', 'createdAt'])
    .execute();

  const postPaths: SitemapItemLoose[] = posts.map((post) => ({
    url: `post/${post.id}`,
    lastmod: post.createdAt.toISOString(),
  }));

  const sitemapItems: SitemapItemLoose[] = [
    { url: '', priority: 1 },
    { url: 'feed', changefreq: EnumChangefreq.WEEKLY },
    { url: 'profile' },
    { url: 'new-post' },
    { url: 'legal/privacy-policy' },
    { url: 'legal/terms-of-service' },
  ];

  const sitemap: SitemapItemLoose[] = [...sitemapItems, ...postPaths];

  const smStream = new SitemapStream({
    hostname: 'https://learnedtoday.app',
  });

  sitemap.forEach((item) => {
    smStream.write(item);
  });

  smStream.end();

  const sitemapXml = (await streamToPromise(smStream)).toString();

  res.setHeader('Content-Type', 'application/xml');
  res.setHeader('CDN-Cache-Control', 'max-age=1800');
  return res.status(200).send(sitemapXml);
}

Example repository

You can check this website's source code which has dynamically generated sitemap! Feel free to check the source code at: https://github.com/KasperiP/til-frontend https://github.com/KasperiP/til-backend