Next.js and Tumblr as a CMS Part 3: Sitemap and RSS
Lauren Ashpole
Posted on June 2, 2022
If you’ve been following along with my series on using Tumblr as a Next.js CMS, last time we looked at fetching data and re-creating all the pages you’d expect to find on your average blog. That tutorial skipped two items that Tumblr normally handles: the sitemap and RSS feed. They require a little extra attention so in this post we’ll build on the earlier code to generate those files.
Sitemap
The basic Tumblr sitemap contains links to your homepage and each individual post. You can also add any other URLs you want to include (I like to throw in a list of featured tags) but for now we'll focus on posts.
In the tumblr.js
file from the last entry, reuse the CLIENT
and getPosts
code to create a method to return all posts:
export async function findAll (limit = 50) {
const client = tumblr.createClient(CLIENT);
const initialResponse = await getPosts(client, limit, 0);
const totalPages = Math.floor(initialResponse.total_posts / limit);
const posts = await [...Array(totalPages).keys()].reduce(async (arr, i) => {
const response = await getPosts(client, limit * (i + 1));
return [ ...(await arr), ...response.posts ];
}, []);
return { ...initialResponse, posts: [ ...initialResponse.posts, ...posts ] };
}
The Tumblr API documentation isn't super clear when it comes to the number of posts you can retrieve in one request. It implies that 20 is the maximum but I haven't found that to be the case in practice so, to make updating easier just in case, the findAll
method accepts limit
as a parameter.
Next, in the pages
directory, add a new file called sitemap.xml.js
. The file will return an empty React component and most of the action takes place in getServerSideProps
where we'll get the posts, use them to generate an XML string, and then switch the Content-Type
to XML with setHeader
.
import { findAll } from '../utils/tumblr';
const Sitemap = () => {};
export async function getServerSideProps ({ res }) {
const response = await findAll();
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url>
<loc>${process.env.NEXT_PUBLIC_BASE_URL}</loc>
<changefreq>weekly</changefreq>
<lastmod>${new Date().toISOString().substring(0, 10)}</lastmod>
</url>
${response.posts.map((post) => `
<url>
<loc>${process.env.NEXT_PUBLIC_BASE_URL}${new URL(post.post_url).pathname}</loc>
<lastmod>${new Date(post.date).toISOString().substring(0, 10)}</lastmod>
</url>
`).join('')}
</urlset>
`;
res.setHeader('Content-Type', 'text/xml');
res.write(sitemap);
res.end();
return {
props: {}
};
}
One thing to note, I store my blog's base URL as an environmental variable so you'll need to either replace NEXT_PUBLIC_BASE_URL
above or add it to your .env
files to get this working.
RSS
For the RSS feed, we'll be using the Feed package to handle the data formatting. You can install it with:
yarn add feed
Create a directory called rss
inside pages
and then add an index.js
file inside of it. By default, the Tumblr feed shows the ten most recent posts so we can use the same find
method we used before for pages.
The file for the RSS feed will look pretty similar to the sitemap. The main difference is that instead of using string interpolation to generate XML we'll follow the example from the Feed docs:
import { Feed } from 'feed';
import { find } from '../../utils/tumblr';
const Rss = () => {};
export async function getServerSideProps ({ res }) {
const response = await find();
const feed = new Feed({
title: 'Your Tumblr Title',
description: 'Your Tumblr description.',
link: process.env.NEXT_PUBLIC_BASE_URL
});
response.posts.forEach((post) => {
feed.addItem({
title: post.title || post.summary,
description: `${post.type === 'photo' ? post.photos.map((photo) => `<img src=${photo.original_size.url} /><br /><br />`) : ''}${post.type === 'video' ? `<iframe width="700" height="383" src="https://www.youtube.com/embed/${post.video.youtube.video_id}" frameborder="0" /><br /><br />` : ''}${post.type === 'link' ? `<a href=${post.url}>${post.title}</a>: ` : ''}${post.type === 'answer' ? `${post.question}: ` : ''}${post.trail[0].content_raw}`,
link: `${process.env.NEXT_PUBLIC_BASE_URL}${new URL(post.post_url).pathname}`,
pubDate: new Date(post.date).toISOString().substring(0, 10),
category: post.tags.map((tag) => { return { name: tag }; })
});
});
res.setHeader('Content-Type', 'text/xml');
res.write(feed.rss2());
res.end();
return {
props: {}
};
}
export default Rss;
To match the Tumblr provided feed, I added some post type specific intros before the raw content in the description text.
That should cover sitemaps and RSS. My next post will dig into some of the bells and whistles that inspired the move to a separate app in the first place like code syntax highlighting and custom social sharing images.
Posted on June 2, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 10, 2024