Since I’ve started collecting notes and highlights here, I’ve been meaning to return them as formatted feeds, RSS being the main one. Well, I got around to it. It was way easier than I remembered, and I even got bonus Atom and JSON feeds out of it.
I’m using Next 13.2 and its new App Directory to generate the site, so this made feeds delightfully simple to implement. In fact, it may be the best experience I’ve ever had for developing content feeds like these. I want to share my walkthrough and results since this is a pretty common task when setting up a new project with Next, and all the existing examples were based in Next’s older
pages generation system.
How to Generate RSS, Atom, and JSON Feeds with Markdown content using Next.js App Directory Route Handlers
I started from the point of already having data-fetching functions for getting all my notes from my CMS (the aptly named
When adding a new function to generate the feed, it simply has to set the top-level properties then run over the notes to add them as entries. I author and store all my notes as Markdown, so for each note I render its body into HTML. Each feed format then gets its own Route Handler, which calls the generator function for the formatted feed. Finally, I update the top-level metadata to include links to the newly added feeds.
Create a Site URL
I quickly realized I needed a little utility function to get the canonical site URL. Since I build and host using Vercel, I want to make sure my site URL corresponds with its preview deploy URL. I used a combination of environment variables to figure that out, using a dedicated
SITE_URL variable with Vercel’s system environment variables to figure out the build’s context and dedicated URL.
Render Markdown to HTML
To render Markdown into HTML, I used the
unified library with the plugins:
remark-parseto parse the Markdown string into an AST
remark-rehypeto convert the Markdown into HTML
rehype-sanitizeto ensure the HTML is safe to render
rehype-stringifyto turn the AST back into a string
This string was then passed as the
content value for each feed item.
Create the Feed
With other site generation frameworks I’ve used, generating feeds has meant writing a template XML file and filling in dynamic values with curly-braced variables, usually with that format’s spec open alongside. This time, I was able to use the
feed package for all the XML authoring. As a result, generating multiple feed formats became a matter of making a function call.
generateFeed function is based on an example provided by Ashlee M Boyer. It creates a feed with proper metadata, then generates each post. Since the Markdown generation runs asynchronously, adding entries needs to happen inside a
Promise.all call. This way,
generateFeed waits to return the feed object until all content has finished generating.
Create the Feed Endpoints
Now here comes the fun part. Creating feed endpoints becomes so simple it’s silly. Using Route Handlers introduced in Next.js 13.2, adding a new endpoint is as simple as creating a folder in the App Directory with the name of the feed file, then creating a
route.ts file inside it.
So, to add the RSS feed, I create the folder
src/app/feeds/rss.xml and then create
route.ts inside it.
To create the Atom and JSON feeds, I follow the same process ensuring that the appropriate method and content type are used in the format’s route handler.
Adding alternates to site metadata
The last step is updating the site’s
<head> to reference these feeds to make them more discoverable to readers. This is made even easier using the App Directory’s Metadata API—also new to Next.js 13.2. In the top-most
layout file in my
app directory, I add an
alternates property to the exported
Now after running
next dev, I can see I have feed files generated at
/feeds/feed.json. I’ve gotten feeds in three different formats with only a few libraries and simple, easily testable functions.
After deploying to production, you can now follow my new notes via:
The flourishing, decentralized Web
The level of productivity I feel when using Next.js, Vercel, and GitHub together is really hard to beat. It feels like the tools are getting out of my way and letting me developer smaller PRs faster.
I’m still a daily RSS user. It’s my preferred way to read on the web. I’m glad to see that there’s still robust library support for RSS and feed generation, at least within the Node ecosystem at least. I don’t think RSS is going anywhere, especially since it powers the entire podcasting ecosystem. It’s great to see the longevity of these open standards.
Speaking of open standards, integrating an ActivityPub server into a Next.js application is something I’m interested in exploring next. It’d be very cool to have a site generated out of an aggregation of one’s own ActivityPub feeds, for example combinining posts from personal micro.blog, Mastodon and Pixelfed into a single syndicated feed.
Seeing all of the recent progress in decentralizing important services has felt so cool. We can still keep the Web wild and weird, empower individuals with more tools for expressing themselves online, and have it all be user-friendly. Content feeds are an important force for good here, so I’m very glad how easy it is these days for even a novice developer to publish them.