How to add an RSS feed to your Next.js WordPress site

I have been working on converting a massive WordPress news site to NextJS. It uses WordPress as Headless CMS and Nextjs’s ISR feature for all the blog posts. NextJS’s ISR feature is awesome but it does not support XML page. In this post, I’ll share how I created a dynamic RSS feed using Next.js SSR and SSR cache option.

The problem

Fetch the latest 20 posts from WordPress and generate an RSS feed using nextJS SSR also implement SSR cache so it does not hit our WordPress API for every request.

NextJS’s ISR feature is awesome and if it allowed XML page support it would solve all the problems but it does not so we have to look into SSR.

Basic RSS feed example

<?xml version="1.0" ?>
    <rss
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom"
    version="2.0"
    >
    <channel>
        <title>Your Site Name</title>
        <atom:link href="https://siteurl.com/feed.xml" rel="self" type="application/rss+xml" />
        <link>https://siteurl.com</link>
        <description>Site description</description>
        <language>en-US</language>
        <lastBuildDate>Latest post Date</lastBuildDate>
    <item>
        <title><![CDATA[ Post Title ]]></title>
        <link>https://siteurl.com/post-url</link>
        <pubDate>Post Publish Date</pubDate>
        <guid isPermaLink="false">Unique post ID</guid>
        <description><![CDATA[ post excerpt]]></description>
        <content:encoded><![CDATA[ post content ]]></content:encoded>
    </item>
    </channel>
    </rss>

This is an example of a typical RSS feed you can also learn more about RSS feeds on w3.org

Fetching RSS feed data from WordPress

getFeedsPosts.js
import featch from "./featch";

async function getFeedsPosts() {
const data = await featch(
    `
    query MyQuery {
        posts(first: 20) {
          nodes {
            id
            uri
            title
            date
            excerpt
            content
          }
        }
      }
      
        `
  );
  return data.posts.nodes;
}
export default getFeedsPosts;
featch.js
const axios = require("axios").default;

export default async function featch(query, { variables } = {}) {
  try {
    const res = await axios({
      url: process.env.NEXT_PUBLIC_WORDPRESS_API_URL,
      method: "post",
      data: {
        query,
        variables,
      },
    });
    const { data } = res.data;
    return data;
  } catch (error) {
    throw new Error("Failed to fetch API");
  }
}

As you can see I’m just fetching the latest 20 posts data. Now we also need a helper function that will take this data and return XML content.

postsTofeed.js
export default async function postsTofeed(blogPosts) {
  let latestPostDate = "";
  let rssItemsXml = "";
  blogPosts.forEach((post) => {
    const postDate = Date.parse(post.date);
    // Remember to change this URL to your own!
    const postHref = process.env.NEXT_PUBLIC_BASE_URL + post.uri;
    if (!latestPostDate || postDate > Date.parse(latestPostDate)) {
      latestPostDate = post.date;
    }
    rssItemsXml += `
        <item>
          <title><![CDATA[${post.title}]]></title>
          <link>${postHref}</link>
          <pubDate>${new Date(post.date).toUTCString()}</pubDate>
          <guid isPermaLink="false">${postHref}</guid>
          <description>
          <![CDATA[${post.excerpt}]]>
          </description>
          <content:encoded>
            <![CDATA[${post.content}]]>
          </content:encoded>
      </item>`;
  });
  return {
    rssItemsXml,
    latestPostDate,
  };
}

The above function returns latestPostDate and XML version of our RSS feed. You can learn more in depth about from Rob Kendal’s post in dev.to

Let’s create the feed.xml page

pages/feed.xml.js
import getFeedsPosts from "@/lib/wordpress/getFeedsPosts";
import postsTofeed from "@/lib/wordpress/postsTofeed";

const FeedPage = () => null;
function Feeds(feed) {
  const { rssItemsXml, latestPostDate } = feed;
  //   console.log(totalPosts);
  return `<?xml version="1.0" ?>
        <rss
          xmlns:dc="http://purl.org/dc/elements/1.1/"
          xmlns:content="http://purl.org/rss/1.0/modules/content/"
          xmlns:atom="http://www.w3.org/2005/Atom"
          version="2.0"
        >
          <channel>
              <title>Your Site Name</title>
              <atom:link href="${
                process.env.NEXT_PUBLIC_BASE_URL
              }/feed.xml" rel="self" type="application/rss+xml" />
              <link>${process.env.NEXT_PUBLIC_BASE_URL}</link>
              <description>Your site description</description>
              <language>en-US</language>
              <lastBuildDate>${new Date(
                latestPostDate
              ).toUTCString()}</lastBuildDate>
              ${rssItemsXml}
          </channel>
        </rss>`;
}
// This gets called on every request
export async function getServerSideProps({ res }) {
  // Fetch data from external API
  const posts = await getFeedsPosts();
  const feed = await postsTofeed(posts);
  //Set page headers
  res.setHeader("Content-Type", "text/xml; charset=utf-8");
  //Set cache for 600s so it wont call our wp on every request.
  res.setHeader("Cache-Control", "s-maxage=600, stale-while-revalidate");
  res.write(Feeds(feed));
  res.end();
  // Pass data to the page via props
  return { props: {} };
}
export default FeedPage;

That’s it now you can go to http://localhost:3000/feed.xml to check your RSS feed.

Important notes

If you check my code I have used .toUTCString() in many places its very important you do so because RSS feed works with Date and Time Specification of RFC 822. Also its best to set cache for SSR pages but not required.

Dipankar Maikap

Dipankar Maikap

I'm a freelance web developer. I work out of Kolkata, India. My favorite number is 77.

Leave a Reply

Your email address will not be published. Required fields are marked *