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
12345678910
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 Filespublic/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;45 config.entry = async () => {6 const entries = { ...(await originalEntry()) };78 // These scripts can import components from the app and use ES modules9 entries['scripts/generate-rss.js'] = './scripts/generate-rss.js';1011 return entries;12 };13 }1415 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><linkrel="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 Files2public/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";67async 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 });1314 const posts = await getAllPosts();1516 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 });3031 fs.writeFileSync("./public/feed.xml", feed.xml({ indent: true }));32}3334generate();
_app.jsjsx
1import React, { Fragment } from 'react';2import Head from 'next/head';34export 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",