Adding a RSS feed to a RemixJS 1.x blog
One of the reasons I chose Remix for this
site was the ability to add server-side code easily to any route, or even to
have no react components returned at all. It kind of reminds me of PHP, or
rails! And so I’m able to add a RSS feed to this blog without having to do
much extra work.
Resource Route
Remix has a feature called
resource routes
that let you create a page that only responds from the server with data, not
with a React component. Perfect for returning JSON, or in this case, an RSS
feed.
To create a resource route, you just need to create a file in the app/routes
directory. The file name will be the route, and the file contents will be the
data that gets returned. I’ve created a file at app/routes/[rss.xml].tsx
that
will return a RSS feed of my posts.
// app/routes/[rss.xml].tsx
export type RssPost = {
title: string;
link: string;
description: string;
pubDate: string;
guid?: string;
author?: string;
};
/**
* Generates an RSS feed from a list of posts.
* @param title Title of the RSS feed
* @param description A description of the RSS feed
* @param link Link to the main page for the RSS feed
* @param posts List of posts to include in the feed
*/
export function generateRss({
description,
posts,
link,
title,
}: {
title: string;
description: string;
link: string;
posts: RssPost[];
}): string {
const rssHeader = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>${title}</title>
<description>${description}</description>
<link>${link}</link>
<language>en-us</language>
<ttl>60</ttl>
<atom:link href="https://keith.is/rss.xml" rel="self" type="application/rss+xml" />`;
const rssBody = posts
.map(
(post) => `
<item>
<title><![CDATA[${post.title}]]></title>
<description><![CDATA[${post.description}]]></description>
<pubDate>${post.pubDate}</pubDate>
<link>${post.link}</link>
<guid isPermaLink="false">${post.link}</guid>
</item>`,
)
.join("");
const rssFooter = `
</channel>
</rss>`;
return rssHeader + rssBody + rssFooter;
}
The generateRss
function takes a title, description, link, and list of posts
and returns a string of the RSS feed. In the loader
function, we’ll use this
function to generate the RSS feed.
// app/routes/[rss.xml].tsx
export const loader: LoaderFunction = async () => {
const posts = await getPosts();
const feed = generateRss({
title: "Keith's Blog",
description: "A blog about web development and other things.",
link: "https://keith.is",
posts: posts.map((post) => ({
title: post.title,
link: `https://keith.is/post/${post.slug}`,
description: post.excerpt,
pubDate: new Date(post.date).toUTCString(),
})),
});
return new Response(feed, {
headers: {
"Content-Type": "application/xml",
"Cache-Control": "public, max-age=2419200",
},
});
};
getPosts()
is my function for getting posts from markdown, but you can
replace that with any method, like connecting to an API withfetch
- The headers ensure that the page is XML, and that it’s cached for 4 weeks
If you browse to /rss.xml
, you’ll see your brand new RSS feed ✨! You can find
mine here. It ends up looking like:
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Keith's Blog</title>
<description>A blog about web development and other things.</description>
<link>https://keith.is</link>
<language>en-us</language>
<ttl>60</ttl>
<atom:link href="https://keith.is/rss.xml" rel="self" type="application/rss+xml" />
<item>
<title><![CDATA[Remix RSS feed]]></title>
<description><![CDATA[Building this site, I had to add a RSS feed for my posts. Using remix resource routes, it's a snap.]]></description>
<pubDate>Thu, 12 Jan 2023 00:00:00 GMT</pubDate>
<link>https://keith.is/post/remix-rss</link>
<guid isPermaLink="false">https://keith.is/post/remix-rss</guid>
</item>
<item>
<title><![CDATA[New year, new site]]></title>
<description><![CDATA[Creating a new site for 2023, focused on writing more and simplifying the process of getting content up. and bigger emojis.]]></description>
<pubDate>Sun, 01 Jan 2023 00:00:00 GMT</pubDate>
<link>https://keith.is/post/new-year-new-site-part-20</link>
<guid isPermaLink="false">https://keith.is/post/new-year-new-site-part-20</guid>
</item>
</channel>
</rss>
Make sure to add the link to your RSS feed to your site. I added it to the
footer of my site, and more importantly as a link
tag.
// app/root.tsx
export const links: LinksFunction = () => {
return [{ rel: "alternate", type: "application/rss+xml", href: "/rss.xml" }];
};