How to Display Your Substack Feed on Your Personal Website (No Plugins Required)
Leveraging RSS feeds on your static website.
If you have a personal website, and you’d like to display your most recent Substack posts on it, then this article is for you. Catch you next time if the acronyms RSS and HTML make your eyes cross.
But Why?
A few months back, I got sick of paying monthly for my WordPress powered personal website. I hadn’t written a new blog post in years, and really wasn’t using it as anything other than a static website. So I migrated all of my existing blog posts to Substack and chose to write all future ones here.
I then rebuilt all of the pages I still found worth keeping in pure HTML, CSS, and JavaScript, switched my hosting to GitHub pages (which is free) and deleted WordPress. The shackles of plugin and template updating had finally been lifted.
Check out my newly renovated website here.
But…
I missed being able to feature my latest posts on my homepage, so I turned to ChatGPT for help and asked some version of:
Can I use my Substack's RSS feed to display my latest post on my static website using JavaScript, HTML, and CSS?
But How?
Sure enough, it turned out I could. I settled on displaying my three latest posts horizontally as in the picture below:
When you hover your mouse over one of the images, it gets a little brighter. So how did I do it?
First I needed to obtain my Substack’s RSS feed. You can get this by adding /feed
to the end of your Substack’s URL like this:
https://mmacfadden.substack.com/feed
Then, I needed to convert the RSS feed to json as serving the RSS feed with pure Javascript caused browser compatibility issues. Fortunately a free resources exists that does this automatically: rss2json.com/. My feed now looked like this:
https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmmacfadden.substack.com%2Ffeed
I then used the JavaScript below (you’d update the bold code with your feed of course):
const feedUrl = `https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmmacfadden.substack.com%2Ffeed`;
async function fetchLatestPosts() {
try {
const response = await fetch(feedUrl);
const data = await response.json();
// Get up to three items from the feed
const latestItems = (data.items || []).slice(0, 3);
if (latestItems.length > 0) {
const postsHTML = latestItems.map(item => {
// Assuming the first image in the content is the featured image
const imageUrlMatch = item.content.match(/<img src="([^"]+)"/);
const imageUrl = imageUrlMatch ? imageUrlMatch[1] : ''; // Default to empty if no image found
// Extract title and subtitle (assuming subtitle is in the description)
const title = item.title;
const subtitle = item.description || 'No Subtitle Provided';
return `
<div class="post col-md-4 my-4">
<a href="${item.link}" class="link_with_image" target="_blank">
<div class="post_image_wrapper">
${imageUrl ? `<img src="${imageUrl}" alt="${title}" class="featured-image">` : ''}
</div>
<div class="post_meta_data">
<h3>${title}</h3>
<p><strong>${subtitle}</strong></p>
</div>
</a>
</div>
`;
}).join('');
// Update HTML with the latest posts details
document.getElementById('latest-posts').innerHTML = postsHTML;
} else {
document.getElementById('latest-posts').textContent = 'No posts available.';
}
} catch (error) {
document.getElementById('latest-posts').textContent = 'Failed to load the latest posts.';
console.error('Error fetching the RSS feed:', error);
}
}
fetchLatestPosts();
I also added an HTML element on the page that I wanted the posts to display within with: id=”latest-posts”
. It looked like this:
<div id="latest-posts" class="row">
Loading latest post...
</div>
That alone did the trick, but it was completely un-styled (accept for the boilerplate Bootstrap 5 styling). So I added the following to make it look pretty:
/*
=================================================
RSS FEED
=================================================
*/
.rss_container{
background-color: #d0e3e4; /* Update to match your accent color */
}
.post{
position:relative;
max-height:250px;
overflow: hidden;
}
.post_image_wrapper{
background-color:#30bdb6;
height:100%;
width:100%;
}
.post img{
width:100%;;
margin-top:-44px;
}
.link_with_image img{
filter: brightness(40%);
transition:.2s;
}
.link_with_image:hover img{
filter: brightness(60%);
}
.post_meta_data{
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
padding:25px;
}
.post h3{
color:#FFF;
text-decoration:none;
font-size:22px;
line-height: 1;
opacity:.9
}
.post p{
color:#FFF;
opacity:.7;
}
If one of my latest posts doesn’t feature an image, it displays as a block with my Substack’s accent color in the background, and that’s it.
Sandbox
Want to play around with this yourself? Check out the link below and swap in your own feed. Enjoy!
Final Thoughts
If the above code doesn’t work for you, you at least now know that this is a thing that is possible. I am sure with a couple of ChatGPT prompts you can get it to work on your website too.
If You Liked This
I wrote another comprehensive article outlining how I backup my Substack to my personal website. Preview the backup here: substack.mmacfadden.com. Then read the article below:
OK, Just One More Thing
I built a simple web app that uses the RSS feeds of your favorite Substack writers to generate three column print ready formatted documents. You can check it out here:
Please don’t display with AI generated images! I for one will read no further, because if the image is fake how can I trust the writing?
Thanks for sharing.