Next.js (SSG) & RSS feed
Publié il y a environ 1 an~3 min
With the proliferation of online content, access to information can sometimes be more time-consuming than one would like... Whether it is about general or more targeted news, there is however a simple way to facilitate daily digest: RSS feeds.
So why not set one up on your (static) website, and thus gain visibility?
Based on a Next.js project (SSG), we will see how to easily add an RSS feed for Markdown-based content.
Prerequisites
- Node.js (>= 10.13)
- A Next.js project (existing one or generated through
npx create-next-app
)
This website was the starting point for this article, so do not hesitate to refer to its source code for more details!
Generate the RSS feed
While it is quite possible to generate the stream file(s) completely manually, it is not necessary to reinvent the wheel! :man-cartwheeling: Here we will rely on the feed
package, which will simplify the writing of our feed generation function:
1import fs from 'fs'2import { Feed } from 'feed'3import { remark } from 'remark'4import html from 'remark-html'5import gemoji from 'remark-gemoji'67import { LOCALES } from '@i18n/constants'8import { getPosts } from '@api/posts'9import { getI18n } from '@api/i18n'1011const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL12const AUTHOR_NAME = process.env.NEXT_PUBLIC_SITE_NAME13const TWITTER_USERNAME = process.env.NEXT_PUBLIC_TWITTER_USERNAME1415const markdownToHtml = (markdown) =>16 remark().use(html).use(gemoji).processSync(markdown).toString()1718const generateRssFeed = () => {19 const author = {20 name: AUTHOR_NAME,21 link: `https://twitter.com/${TWITTER_USERNAME}`,22 }2324 LOCALES.forEach((lang) => {25 const { description } = getI18n(lang, 'home').home2627 const feed = new Feed({28 title: `${AUTHOR_NAME}'s blog feed`,29 description: Object.values(description).join(' '),30 id: `${SITE_URL}/${lang}`,31 link: `${SITE_URL}/${lang}`,32 language: lang,33 generator: 'Next.js using Feed',34 feedLinks: {35 rss2: `${SITE_URL}/${lang}/feed.xml`,36 },37 author,38 })3940 const posts = getPosts(['title', 'excerpt', 'content'], lang)4142 posts.forEach((post) => {43 feed.addItem({44 title: post.title,45 id: `${SITE_URL}/${lang}/blog/${post.slug}`,46 link: `${SITE_URL}/${lang}/blog/${post.slug}`,47 description: post.excerpt,48 content: markdownToHtml(post.content),49 date: new Date(post.date),50 author: [author],51 })52 })5354 fs.mkdirSync(`./public/${lang}`, { recursive: true })55 fs.writeFileSync(`./public/${lang}/feed.xml`, feed.rss2(), 'utf8')56 })57}5859generateRssFeed()
The code is split into two quite simple parts: first we generate the flow with its general information, then we loop through our content (posts) to generate our items.
The whole is then written into an .xml
file, in RSS 2.0 format. Several formats are possible, however its excellent support makes the addition of other formats often superfluous.
Our content here is written using Markdown syntax. By relying on the remark
ecosystem, we can easily add to our feed items the (exhaustive) content of our articles, converted to HTML
. This is often a recommended practice, although not necessary.
Finally, in the case of a multilingual site, we'll just have to adapt the code, to generate as many files as supported languages.
Helping robots
To make life easier for browsers and other indexing robots, and to help them find our RSS feed, we should remember to add a link to it in the <head />
of our document:
1<link2 rel="alternate"3 type="application/rss+xml"4 title={`${AUTHOR_NAME}'s blog feed`}5 href="/feed.xml"6/>
Updating the build script
Now that everything is in place, we still have to call this function. In the context of a SSG site, this call will be made, logically, during the build phase.
Our RSS script uses ES modules (ESM), to allow the import of functions used elsewhere in the project (such as content fetching). To use it in CLI, we need first to adapt our Webpack configuration via the next.config.js
file:
1module.exports = {2 // ...34 webpack: (config, { dev, isServer }) => {5 if (!dev && isServer) {6 const originalEntry = config.entry;78 config.entry = async () => {9 const entries = await originalEntry()1011 // These scripts can import components from the app12 // and use ES modules13 return { ...entries, 'scripts/rss-generate': './src/scripts/rss.js' }14 }15 }1617 return config18 }19}
Our script will thus be compiled by Webpack in the .next/server
folder. All that remains is to call it via another script from our package.json
file, which will take care of generating our RSS feed:
1"scripts": {2 // ...3 "build": "next build && npm run rss:generate",4 "rss:generate": "node ./.next/server/scripts/rss-generate"5}
🎉
Et voilà ! Adding an RSS feed is a real plus for a website (especially a blog or other news site), allowing users to follow our content in a simple way, without having to juggle multiple tabs... It's also a good way to improve our visibility online, so why deprive yourself? 🙂