Make Your Website’s RSS More Beautiful (and Useful) with XSLT

Unfortunately, diminishing browser support means this beauty is fleeting. But it’s still worth checking out in the meantime.
A collage of blue RSS logos with an orange one standing out in the center

Last week, I wrote about the importance of RSS as a tool for breaking free from social media algorithms. Unfortunately, less-tech-savvy users might find RSS daunting and difficult to work with, due in large part to web browsers no longer offering built-in support like they once did.

Older versions of Chrome, Firefox, et al., would alert users when websites offered RSS web feeds and even help users subscribe to them. These days, however, browsers no longer alert you to the existence of feeds, and should you enter a feed URL, they’ll do one of three things: display a wall of code, download an XML file to your computer, or prompt you visit the App Store to find something that’ll let you view RSS.

None of those are helpful or convenient, and as a result, a lot of users are likely missing out on the benefits of using RSS to keep abreast of their favorite websites. Fortunately, we developers can offer our users a much better RSS experience.

Note: As with my previous article, I’ll be using “RSS” as a catchall term for web feeds, which can, in fact, be generated using either the RSS or Atom format.


Enter XSLT

Web feeds are essentially XML files that follow specific formats for describing structured website content like blog posts and news articles. For example, here’s a simplified version of Opus’s feed, which is generated using Atom:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/feed/feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>Opus</title>
	<link rel="self" href="https://opus.ing/feed" />
	<link href="https://opus.ing/" />
	<updated>2026-02-19T10:30:49-06:00</updated>
	<id>https://opus.ing/feed</id>
	<author>
		<name>Jason Morehead</name>
	</author>
	<entry>
		<id>[Entry URL Goes Here]</id>
		<link rel="alternate" href="[Entry URL Goes Here]" />
		<title type="html">[Entry Title Goes Here]</title>
		<published>2026-02-18T17:39:00-06:00</published>
		<updated>2026-02-18T17:39:43-06:00</updated>
		<author>
			<name>Jason Morehead</name>
			<uri>https://opus.ing/</uri>
		</author> 
		<summary type="html">[Entry Summary Goes Here]</summary>
		<content type="html"><![CDATA[[Entry Content Goes Here]]]></content>
	</entry>
	<entry>
		<id>[Entry URL Goes Here]</id>
		<link rel="alternate" href="[Entry URL Goes Here]" />
		<title type="html">[Entry Title Goes Here]</title>
		<published>2026-02-18T17:39:00-06:00</published>
		<updated>2026-02-18T17:39:43-06:00</updated>
		<author>
			<name>Jason Morehead</name>
			<uri>https://opus.ing/</uri>
		</author> 
		<summary type="html">[Entry Summary Goes Here]</summary>
		<content type="html"><![CDATA[[Entry Content Goes Here]]]></content>
	</entry>
	<entry>
		<id>[Entry URL Goes Here]</id>
		<link rel="alternate" href="[Entry URL Goes Here]" />
		<title type="html">[Entry Title Goes Here]</title>
		<published>2026-02-18T17:39:00-06:00</published>
		<updated>2026-02-18T17:39:43-06:00</updated>
		<author>
			<name>Jason Morehead</name>
			<uri>https://opus.ing/</uri>
		</author> 
		<summary type="html">[Entry Summary Goes Here]</summary>
		<content type="html"><![CDATA[[Entry Content Goes Here]]]></content>
	</entry>
</feed>

At the top of the feed is a bunch of information about the feed, including its title and URL. That’s followed by a series of <entry> tags, each of which contains information about a single blog post, including title, URL, author, and content.

It’s pretty simple, but that simplicity is precisely what makes RSS feeds so useful for sharing web content. For less-tech-savvy users, though, all of that XML is tantamount to gibberish, especially when displayed in a web browser as the aforementioned wall of code. We can do better, though. We can take XML like the above example and convert it into an HTML webpage that users can actually interact with, same as any other page on our website.

The key to that is XSLT, which stands for “Extensible Stylesheet Language Transformations.” As its name implies, XSLT transforms XML into other formats to make it more presentable and useful. For our purposes, that format is HTML. (Or, to be more precise, XHTML.)

Returning to the above XML code, check out the second line:

<?xml-stylesheet href="/feed/feed.xsl" type="text/xsl"?>

That links to an XSLT stylesheet, which is where the magic happens. An XSLT stylesheet looks a lot like a regular HTML file, complete with <html>, <head>, and <body> tags. But if you view the source of Opus’s XSLT stylesheet, you’ll see XSL tags sprinkled throughout, such as <xsl:for-each select="atom:entry">. (Andrew Stiefel goes into more detail about XSLT stylesheets.)

Those XSL tags identify and pull in pieces from the feed’s XML (e.g., post titles and summaries) and plugs them into the HTML layout specified in the XSLT stylesheet. That is then applied to the feed, effectively transforming it into a webpage. The process is not all that dissimilar to using a language like PHP to loop through the results of a database query, format them, and display them in a website template.

Based on my limited experience, this is actually a pretty basic XSLT implementation. Nevertheless, it still took me awhile to figure out how to get it all to work. For starters, my XML seemed to completely ignore the XSLT stylesheet’s existence, even though I verified and re-verified that my code was correct. After much confusion, I finally discovered the issue: a feed needs to be served as application/xml for XSLT to work, but Craft defaults to serving Atom feeds as application/atom+xml. Once I told Craft to serve the feed as application/xml, everything began to click, and I’m really pleased with the final result.


The Browsers Strike Again

XSLT is a really simple way to immediately improve how users can access your website’s content. Unfortunately, we can’t have — or keep — nice things.

Citing security and maintenance concerns, web browsers have announced plans to drop support for XSLT, with Chrome dropping support by year’s end. Justin Jackson has blogged at length about this, highlighting the potential ramifications and lack of decent workarounds. RSS certainly took a blow when browsers dropped built-in support years ago, and this latest decision will further ensure that RSS remains a niche technology. Which is a shame because it could be so much more and help out so many more people.

It also means that all of the work I did XSLT-ifying Opus’s web feed will be irrelevant this time next year. Even so, I don’t regret doing it. In keeping with my website-as-hot rod metaphor, it was fun to fiddle around under the hood and experiment with something new. But more importantly, beautifying Opus’s web feed just felt like the right thing to do.

As developers, we should want to make every aspect of our websites as accessible and human-friendly as possible. That’s doubly true for those aspects that can specifically improve people’s ability to access and experience our websites’ content. It’s just a shame that this particularly useful — and unique — solution won’t be available to our users for much longer.

Enjoy reading Opus? Want to support my writing? Become a subscriber for just $5/month or $50/year.
Subscribe Today
Return to the Opus homepage