Adding an RSS Feed in Next.js 13 (App Router)

In this article, we'll setup an RSS feed in Next.js while also using the new App Router. The rss npm package will be used to generate the feed in xml format, then we can render it in the browser.

Before We Begin, What is an RSS Feed?

An RSS (Really Simple Syndication) feed is an online file that contains details about every piece of content a site has published. Each time a site publishes a new piece of content, details about that content—including the full-text of the content or a summary, publication date, author, link, etc. — are automatically generated in the file and displayed in reverse chronological order.

© Jessica Greene

Setup a New Next.js Project

Verify that you are using Node.js v16 and up, then we can now setup the project:

npx create-next-app@latest --experimental-app

Here's how I setup mine:

Finally, run npm run dev and view the welcome page:

Adding a Posts Listing Page and Populating it with Data

Ideally you already setup the post listing page, but for an added context, we will be doing it, we'll pull some data from the News API and we'll use Tailwind.css to style the pages and components.

First, let's create a new page, place it inside src/app/blog/page.tsx. In the file let's define a getArticles function so we can populate the page with dummy data:

// We can get this by creating an account in the news api website: https://newsapi.org/register
const API_KEY = "26xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

async function getArticles(): Promise<Record<string, any>[]> {
  const response = await fetch(
    `https://newsapi.org/v2/everything?q=chatgpt&from=2023-03-07&sortBy=publishedAt&apiKey=${API_KEY}`
  );

  const { articles } = await response.json();

  return articles;
}

Now, we can render it on to the page:

async function getArticles(): Promise<Record<string, any>[]> {}

export default async function Page() {
  const articles = await getArticles();

  return (
    <div className="max-w-full px-4 py-6 mx-auto sm:max-w-3xl bg-zinc-100">
      <h1 className="text-2xl font-semibold text-center text-zinc-900">
        Articles
      </h1>

      <div className="px-3 mt-4 space-y-3">
        {articles.map((article) => (
          <article key={article.url}>
            <h2 className="text-lg font-medium text-zinc-900">
              {article.title}
            </h2>

            <p className="text-sm text-zinc-700">{article.content}</p>

            <time
              className="text-sm italic font-medium text-zinc-900"
              dateTime={article.publishedAt}
            >
              {new Date(article.publishedAt).toDateString()}
            </time>
          </article>
        ))}
      </div>
    </div>
  );
}

Let's view it in the browser: http://localhost:3000/blog

Adding the RSS Feed

We now have the data, let's add the rss feed, first let's move the getArticles function to a separate file, place it inside `src/app/data/article.data.ts:

const API_KEY = "26xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

export async function getArticles(): Promise<Record<string, any>[]> {
  const response = await fetch(
    `https://newsapi.org/v2/everything?q=chatgpt&from=2023-03-07&sortBy=publishedAt&apiKey=${API_KEY}`
  );

  const { articles } = await response.json();

  return articles;
}

Now, let's import it to the page.tsx:

import { getArticles } from "@/data/article.data";

export default async function Page() {
 // ... 
}

Ideally, we want to render the feed in xml format by visiting http://localhost:3000/rss.xml, In the new Next.js's App Router, anything inside src/app can contain a directory and a route.ts (or route.js) file and this will be routed even if it's a page an API endpoint or in our case an RSS feed. Now let's add a new route src/app/rss.xml/route.ts, we can then generate the feed there by returning an instance of the Request class.

First, we need to install the RSS package via npm and it's corresponding typings (skip if you're not using Typescript):

npm install rss
npm install -D @types/rss

Then, let's define the GET route handler and populate the Rss with the articles:

import Rss from "rss";

import { getArticles } from "@/data/article.data";

const SITE_URL = "http://localhost:3000";

export async function GET() {
  const articles = await getArticles();

  const feed = new Rss({
    title: "Example blog",
    description: "Lorem ipsum dolor sit amet.",
    feed_url: `${SITE_URL}/rss.xml`,
    site_url: SITE_URL,
    language: "en",
  });

  articles.forEach((article) => {
    feed.item({
      title: article.title,
      description: article.description,
      url: `${SITE_URL}/blog/${article.slug}`,
      guid: `${SITE_URL}/blog/${article.id}`,
      date: article.publishedAt,
    });
  });

  return new Response(feed.xml(), {
    headers: {
      "Content-Type": "application/xml",
    },
  });
}

Finally, we can view it in the browser:

And that's about it! 🎉