Styling an RSS Feed using XSL

Styling an RSS Feed using XSL

Making my RSS-Feed prettier to look at

Written by: David Moll

12/29/2024

This post is over one year old and may contain outdated information.

If you have questions or encounter problems, please reach out to me via Bluesky, LinkedIn, or GitHub.

Opening

Recently I learned from Bob Monsour that its possible to style an RSS-Feed to make it prettier to look at for humans. It is a miracle that I didn't notice this earlier considering Firefox literally notifies you about a missing stylesheet when looking at a .xml-file.

Firefox notifying me about a missing stylesheet for an xml-file

Bob was so nice to list multiple sources to explain how this works, if you want to read them I linked them here[1]. Here I am going to explain in my own words how I styled my RSS-Feed using XSL.

Note: Creating a XSL-stylesheet requires basic knowledge of CSS and HTML

What is XSL?

XSL is a language to format stylesheets, similar to CSS. Based on CSS2 which was initially released in 1998, XSL allows you to define how a XML-document should be displayed. It also adds a transformation-language called XSLT which allows the user to format data in different ways and can be used to for example display XML-data in an HTML-file. This doesn't mean that XSL was intended to replace CSS, it was rather created to format data on the server while CSS formats it for the client.

Use XSL in an RSS-Feed

Note: I use RSS in my example but XSL also works for Atom-feeds. The markup is a little bit different, for a specific example for Atom you can use Bob Monsour's XSL-file[1:1].

First we need to link the XSL-stylesheet to the RSS-file. This can be done in a similar way to how its done with CSS and HTML. Place following line above the <rss> object, adjust the path and you are already done with the first step:


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

With that done we can get started with the styling and formatting. Take this empty template as a starting-point


<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="3.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:atom="http://www.w3.org/2005/Atom">

	<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>

	<xsl:template match="/">
		<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
			<head>
				<title>Title goes here</title>
				<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
				<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
				<style type="text/css">
				</style>
			</head>
			<body>
			</body>
		</html>
	</xsl:template>
</xsl:stylesheet>
rss.xls

Here you can already see an empty <style> and <body>-tag. As a simple example let's change the background-color of the rendered page:


<style type="text/css">
	body {
    	background-color: #181a1b;
	}
</style>
rss.xsl

As you can see this is just basic CSS. You can almost any CSS-feature you want, I however noticed that features, despite beeing supported by the browser, such as light-dark() do not work. But I do suspect that down the line those features will not be a problem anymore.

Now we get to the really interesting part. In the body we can use basic HTML with no issues, but we can expand on them with XSL. This allows us to format data, use for-loops and many many more tricks. Let's go through the important ones that we need for our RSS-Feed.

Getting and transforming items

In this blog I use 11ty which allows me to save and access data in collections. We can render all of them in our RSS-file using Nunjuck


{%- for post in collections.posts | reverse %}
    {%- set absolutePostUrl = post.url | absoluteUrl(metadata.url) %}
     <item>
      <title>{{ post.data.title }}</title>
      <link>{{ absolutePostUrl }}</link>
      <description>
        {{ post.data.description }}>
      </description>
      <author>{{ metadata.author.url }} ({{ metadata.author.name }})</author>
      <pubDate>{{ post.date | dateToRfc822 }}</pubDate>
      <guid>{{ absolutePostUrl }}</guid>
      <source url="{{ metadata.url }}">{{ metadata.title }}</source>
    </item>
{%- endfor %}
rss.xml

Now if we want to modify the display of every single <item> we can use <xsl:for-each select="/rss/channel/item"></xsl:for-each>. In this for-each loop we can access each so-called element of an item and use it. Let's take as a simple example the title, description and author of every item and display them in an unordered list using <xsl:value-of:


<xsl:for-each select="/rss/channel/item">
    <ul>
        <li>
            <xsl:value-of select="title"/>
            <ul>
                <li><xsl:value-of select="description"/></li>
                <li><xsl:value-of select="author"/></li>
            </ul>
        </li>
    </ul>
</xsl:for-each>
rss.xsl

We can also create hyperlinks with some tricks


<a hreflang="en" target="_blank">
	<xsl:attribute name="href">
		<xsl:value-of select="link"/>
	</xsl:attribute>
		Read the article
</a>
rss.xsl

Now let's take as an example the date of an item and transform it. By default I get the date in Rfc822-format meaning I get dates such as Sun Feb 08 2026 08:28:11 GMT+0000 (Coordinated Universal Time). This doesn't look as good as it should so it we can treat the date as a string, cut it apart and concatinate it back together to get something better looking:


<p>
	Published on:
	<xsl:value-of select="concat(substring(pubDate, 6, 2), ' ', substring(pubDate, 9, 3), substring(pubDate, 12, 5))" />
</p>
rss.xsl

In a more perfect world where we get the date in yyyy-mm-dd we could do all kinds of things like using the XLST-functions format-dateTime, format-date, and format-time but alas we are forced to work with string-concatination. If you do are lucky enough to work with those formats you can read more about those functions in the w3-docs.

Advantages and drawbacks

XSL allows you to make anx XML-file much more pleasant to read when accessing it and with the wide variety of transformations offered by XSLT backed up by the freedom of CSS you can do some really crazy stuff with it. And in the end the XML-file will continue to be just that and can be viewed in its raw-form with out issues.

Using XSL to style an HTML-page is rarely used so it leads to different implementations on how to handle such a case across different browsers. If you try to save the rendered page you will download the HTML-page in Firefox but the XML-file in Chrome. While this is fine for RSS-Files this may become a problem for XML-files that are supposed to be downloaded. Thanks to Robb Knight for this observation

Other examples

Footnotes


  1. How to make your RSS feed pretty and Style your RSS feed ↩︎ ↩︎