Guillermo de la Puente

How to generate an RSS feed with Next.js

Development

Published on

RSS Feed RSS Feed

RSS feed over a newspaper
RSS feed over a newspaper

While RSS feeds are definitely on the decline, it might be a significant amount of your readers that prefers to use an RSS reader, so you want to implement it on your site. Or you’re a curious developer and you find pleasure making your blog more technically complete (like me!). With Next.js plus one dependency to produce the XML, today it’s so easy to produce an RSS feed. Let me show you.


Using Next.js to produce the feed

With Next.js, we have a couple of options:

  • Postbuild: producing the feed after building and placing the file in /public/rss.xml

  • Server-side Rendering: Creating a page /pages/rss.xml.tsx that dynamically produces the feed and returns it

  • Static Generation: Creating a page /pages/rss.xml.tsx that produces the feed and returns it


Postbuild generation might be enough

If your website URLs and content can only change with deployments, creating the feed on a postbuild script might be enough.

There’s a conveniently named package: next-rss. It should work out of the box, just placing a next-rss.js config file in the project’s root directory. However, I don’t recommend using it as it has issues and it seems unmaintained.

I made a fork package, fixing the issue and extending the ability to customize feed items: https://www.npmjs.com/package/@guillermodlpa/next-rss

But unless this is exactly what you’re looking for, I still don’t recommend this approach just because of how easy it is to do it yourself in a server-side rendered page.


Generating the RSS feed with Server-side Rendering

Check out a live example here: https://guillermodlpa.com/rss.xml

Instructions:

  1. Create a new page in your Next.js project: /pages/rss.xml.tsx

  1. Make the page component export nothing, like this:

export default function RssPage() {}
  1. In getServerSideProps, fetch all the blog posts for your feed.

  1. Use one library to produce the feed XML: rss or feed.

  1. Set the Cache-Control header with a s-maxage value so that the answer is cached in the Vercel network (or your custom cache if not using Vercel).

  1. Set the content type of the response to XML:

ctx.res.setHeader("Content-Type", "text/xml");
  1. End the response and return an empty props object:

ctx.res.end();

return { props: {} };

Here’s the implementation of the RSS feed for this blog:

/**
 * This is a dynamic RSS feed rendered on the server with all blog posts
 * We use SSG with a cache of a few minutes instead of statically generating the file because we want
 * new blog posts and edits to automatically show up here
 */

import { getServerSideSitemap } from "next-sitemap";
import { GetServerSideProps } from "next";
import { fetchBlogPosts } from "../lib/notionClient";
import getConfig from "next/config";
import { getBlogPostPath, PATH_RSS } from "../constants/paths";
import RSS from "rss";

const { publicRuntimeConfig } = getConfig();

export const getServerSideProps: GetServerSideProps = async (ctx) => {
  const blogPosts = await fetchBlogPosts();

  const feed = new RSS({
    title: `Guillermo de la Puente's Blog`,
    description: `I blog about frontend patterns and projects, management, and digital nomad lifestyle`,
    language: "en",
    copyright: `©${new Date().getFullYear()} Guillermo de la Puente`,
    site_url: publicRuntimeConfig.SITE_URL,
    feed_url: `${publicRuntimeConfig.SITE_URL}${PATH_RSS}`,
  });

  blogPosts.forEach((blogPost) => {
    feed.item({
      guid: blogPost.id,
      title: blogPost.title,
      description: blogPost.excerpt,
      url: `${publicRuntimeConfig.SITE_URL}${getBlogPostPath(blogPost.slug)}`,
      date: new Date(blogPost.datePublished),
      categories: blogPost.tags.map((tag) => tag),
      enclosure: {
        url: blogPost.imageSrc,
      },
    });
  });

  const cacheMaxAgeUntilStaleSeconds = 60 * 60; // 1 minute
  const cacheMaxAgeStaleDataReturnSeconds = 60 * 60 * 60; // 60 minutes
  ctx.res.setHeader(
    "Cache-Control",
    `public, s-maxage=${cacheMaxAgeUntilStaleSeconds}, stale-while-revalidate=${cacheMaxAgeStaleDataReturnSeconds}`
  );

  ctx.res.setHeader("Content-Type", "text/xml");
  ctx.res.write(feed.xml());
  ctx.res.end();

  return { props: {} };
};

// Default export to prevent next.js errors
export default function RssPage() {}

Generating the RSS feed with Static Generation

With just a few tweaks to the implementation above, you can change it to be statically generated. However, remember that you’d need to write some code to revalidate the page on-demand when there are changes on the blog posts! That can be inconvenient if you just have it to implement it for RSS feed, but if you’re doing it already for the blog list page and the sitemap, then you might as well use SG and on-demand revalidation here too.

Happy coding!

Back to all posts