My profile picture. My mom says I'm a handsome boy!
← All Posts

Building a Next.js Blog: RSS

#Code, #Guides, #Projects

I love RSS, and I wanted to make sure I supported it with this redesign. There are two approaches to adding an RSS feed to a Next.js site - you can generate the RSS server-side and return it for each request, or add a build step that statically generates the file. I'll cover statically generating it here.

Generating the File

Next.js doesn't currently support different response types when you statically generate files. Since RSS is XML, we won't be able to create it under the pages/ directory - instead we'll create a file in the public/ directory during the build step.

First, let's install the rss npm package to help create the RSS XML structure.

terminal
$npm install rss

Then let's create a new scripts/generate-rss.js file with the following:

scripts/generate-rss.jsjsx
1import fs from "fs";
2import RSS from "rss";
3
4const feed = new RSS({
5 title: "Website Title",
6 site_url: "https://mydomain.com",
7 feed_url: "https://mydomain.com/feed.xml",
8});
9
10fs.writeFileSync("./public/feed.xml", feed.xml({ indent: true }));

Running this script will create a new public/feed.xml file with some minimal information in it! Since we'll be generating this file each time we build we don't want to check it into source control. Let's go ahead and add it to our .gitignore file.

yaml
# Generated Files
public/feed.xml

The next step is adding posts to the feed. This snippet assumes a structure similar to the one I wrote about in my Building a Next.js Blog: Static MDX post but it should be easy to tweak as long as you have the ability to get an array of all of your posts. In the function, we need to iterate over these posts and add them to the feed object.

javascript
const posts = getPosts();
posts.forEach((post) => {
const { title, url, date, content } = post;
feed.item({
title: title,
guid: url,
url: url,
date: date,
description: content,
});
});

Next we'll want to hook this script up to Webpack (this lets us use ESM and import code from our Next.js codebase). To do this we need to add an entry to the next.config.js file. We'll only add it during the build step for performance reasons.

next.config.jsjavascript
1 webpack: (config, { dev, isServer }) => {
2 if (!dev && isServer) {
3 const originalEntry = config.entry;
4
5 config.entry = async () => {
6 const entries = { ...(await originalEntry()) };
7
8 // These scripts can import components from the app and use ES modules
9 entries['scripts/generate-rss.js'] = './scripts/generate-rss.js';
10
11 return entries;
12 };
13 }
14
15 return config;
16 },

Now we can call the webpack-generated script by adding it to the build step in the package.json file:

json
"build": "next build && node ./.next/server/scripts/generate-rss.js",

Finally we need to add a <link> reference! I include the following in my _app.js file

jsx
<Head>
<link
rel="alternate"
type="application/rss+xml"
title="RSS"
href="/feed.xml"
/>
</Head>

Final Code

I've included my version of generate-rss.js here, which compiles MDX to HTML for use with RSS. Depending on the format of your content you might need to make some revisions to it.

.gitignoreyaml
1# Generated Files
2public/feed.xml
scripts/generate-rss.jsjsx
1import fs from "fs";
2import RSS from "rss";
3import ReactDOMServer from "react-dom/server";
4import MDX from "../components/MDX";
5import { getAllPosts } from "../lib/posts";
6
7async function generate() {
8 const feed = new RSS({
9 title: "Website Title",
10 site_url: "https://mydomain.com",
11 feed_url: "https://mydomain.com/feed.xml",
12 });
13
14 const posts = await getAllPosts();
15
16 posts.forEach((post) => {
17 const { frontmatter, MDXContent } = post;
18 feed.item({
19 title: frontmatter.title,
20 guid: frontmatter.slug,
21 url: frontmatter.slug,
22 date: frontmatter.date,
23 description: ReactDOMServer.renderToStaticMarkup(
24 <MDX>
25 <MDXContent />
26 </MDX>
27 ),
28 });
29 });
30
31 fs.writeFileSync("./public/feed.xml", feed.xml({ indent: true }));
32}
33
34generate();
_app.jsjsx
1import React, { Fragment } from 'react';
2import Head from 'next/head';
3
4export default function App({ Component, pageProps }) {
5 return (
6 <Fragment>
+ <Head>
+ <link
+ rel="alternate"
+ type="application/rss+xml"
+ title="RSS"
+ href="/feed.xml"
+ />
+ </Head>
15 <Component {...pageProps} />
16 </Fragment>
17 );
18}

Add the script to webpack by adding this to next.config.js:

next.config.jsjavascript
1module.exports = {
+ webpack: (config, { dev, isServer }) => {
+ if (!dev && isServer) {
+ const originalEntry = config.entry;
+
+ config.entry = async () => {
+ const entries = { ...(await originalEntry()) };
+
+ // These scripts can import components from the app and use ES modules
+ entries['scripts/generate-rss.js'] = './scripts/generate-rss.js';
+
+ return entries;
+ };
+ }
+
+ return config;
+ },
18}
package.jsonjson
1 "build": "next build && node ./.next/server/scripts/generate-rss.js",