Feed at its best
RSS, Atom, feed?
When it comes to web syndication, many names are floating around, some used synonymously. RSS is often seen as the name for the functionality of a feed itself, but it is not; it is rather one way of delivering website content in a standardized way. Another one is Atom, which goes a bit further by being a standard for a feed format and publishing protocol and is seen as an alternative to RSS.
Both do the same thing in slightly different ways. Without going into too much detail, Atom gives more flexibility and is an excellent web standard; this is the format I added to my site.
Awesome feed
Going under the radar of many, website feeds are amazing for a lot of reasons. For me, it is being able to subscribe to the exact content I like. Everything from art, games and game development, programming, books, etc., things I actually want to read.
If a website serves full content via the feed, I can even read everything inside my reader app*. Other sites, delivering only headlines and/or summaries, create a content stream for me that I control and can dive deeper into if I so desire.
The only thing I would wish for is that those feeds were more easily explorable.
Feed visibility, feed availability
I find myself quite often wondering if a website, blog, or portfolio has a feed. There is not always a link, icon, or reference mentioned. Besides not being visible, a lot of sites do not offer any kind of feed; still, there are way more sites out there that do.
If I am not sure, I use my reader to find out — I paste the link into it, and if a feed can be discovered, it will tell me. This works surprisingly well, more often than not. The way this "auto discovery" is done is through a meta-tag in the website's head, linking to the feed's XML file.
<link
rel="alternate"
type="application/atom+xml"
title="My Blog"
href="/feed.xml"
/>
type
needs to be application/rss+xml
.
My blog as feed
As I love using feeds, I for sure needed to serve my blog as a feed as well. The base XML file structure for the Atom feed will look like the following.
<?xml version="1.0" encoding="utf-8" ?>
<feed
xmlns="http://www.w3.org/2005/Atom"
xml:base="absolute/url/to/blog">
<title>Martin Fieber's Blog</title>
<subtitle>
Some extra information ...
</subtitle>
<link
href="absolute/url/to/feed.xml"
rel="self" />
<link href="absolute/url/to/blog" />
<icon>absolute/url/to/favicon.ico</icon>
<logo>absolute/url/to/logo.png</logo>
<updated>2022-11-05T10:10:10Z</updated>
<id>absolute/url/to/blog/</id>
<author>
<name>Martin Helmut Fieber</name>
<email>info@martin-fieber.se</email>
</author>
<entry>
<title>Post title</title>
<link href="absolute/url/to/post" />
<updated>2022-10-20T00:00:00Z</updated>
<id>absolute/url/to/post</id>
<summary xml:lang="en" type="html">
Content summary
</summary>
<content xml:lang="en" type="html">
Full content
</content>
</entry>
<!-- more entries ... -->
</feed>
The first tags set up the details of the feed connected to my blog: title and subtitle, a link to the feed and the blog, icons, last update date, unique identifier, and information about me as the author of the blog.
A post is defined through the contents of the entry
tag, the base
being the title of the post, a link, an update date, and a unique identifier.
The summary
and content
tags are respectively for a
short post summary and the full content of the post — there can be both,
one, or none.
I added both, as I want people to be able to read the whole post from any reader app.
As I use
Eleventy, I will take advantage of it and generate the feed file from my posts using
the
Eleventy RSS plugin. For this, I set up a feed.njk
file, using the
Nunjucks template language, with some data defining the target file name feed.xml
as well
as my site and blog URL.
<!-- /feed.xml -->
---json
{
"permalink": "feed.xml",
"eleventyExcludeFromCollections": true,
"metadata": {
"blogUrl": "https://martin-fieber.de/blog/",
"siteUrl": "https://martin-fieber.de/"
}
}
---
<?xml version="1.0" encoding="utf-8"?>
<feed
xmlns="http://www.w3.org/2005/Atom"
xml:base="{{ metadata.blogUrl }}">
<!-- ... -->
</feed>
The first section separated by ---
is
front matter data
as JSON. Inside the feed tags are the feed title, author, and some more.
<title>Martin Fieber's Blog</title>
<subtitle>...</subtitle>
<link
href="{{
permalink
| absoluteUrl(metadata.siteUrl)
}}"
rel="self"/>
<link href="{{ metadata.blogUrl }}"/>
<icon>
{{ metadata.siteUrl }}favicon.ico
</icon>
<logo>
{{ metadata.siteUrl }}assets/favicon/icon-192.png
</logo>
<updated>
{{
collections.posts
| getNewestCollectionItemDate
| dateToRfc3339
}}
</updated>
<id>{{ metadata.blogUrl }}</id>
<author>
<name>Martin Helmut Fieber</name>
<email>info@martin-fieber.se</email>
</author>
To generate the entries for my post, I want to have the latest post on top, so I need to reverse the order of my post collection. I also use absolute URLs everywhere.
{%-
for post in collections.post
| reverse
%}
{%-
set absolutePostUrl = post.url
| absoluteUrl(metadata.blogUrl)
%}
<entry>
<title>{{ post.data.title }}</title>
<link href="{{ absolutePostUrl }}"/>
<published>
{{ post.date | dateToRfc3339 }}
</published>
<updated>
{{
post.data.lastmod | dateToRfc3339
if post.data.lastmod
else post.date | dateToRfc3339
}}
</updated>
<id>{{ absolutePostUrl }}</id>
<summary xml:lang="en" type="html">
{{
post.data.summary
| htmlToAbsoluteUrls(absolutePostUrl)
}}
</summary>
<content xml:lang="en" type="html">
{{
post.templateContent
| htmlToAbsoluteUrls(absolutePostUrl)
}}
</content>
</entry>
{%- endfor %}
You can take a look at the full Nunjucks template file I use to generate my Atom feed on GitHub.
XML in style
Taking a look at my feed file directly in the browser, it looks somewhat like the following.

Even though this is a fairly standard view for XML on the web, it is not very user-friendly and little useful in general. With this, greet XSLT (Extensible Stylesheet Language Transformations), a mouthful of words where "Stylesheet" is the important part for me.
XSL … T?
To keep it short, XSLT is an XML-like language to transform XML documents. Think of reformatting a document, or, in that case, styling a document. Taking the XML feed and transforming it into an HTML document gives me the option to use styles I've already defined on my website.
When defining a xsl:stylesheet
I need to define what namespaces I
want to use — xsl
for operations like loops with
xsl:for-each
, and atom
, used to access elements in
the feed. Those are defined by referencing the associated schema. I could
really use any name, but I stick to xsl
and atom
.
<xsl:stylesheet
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:atom="http://www.w3.org/2005/Atom">
In my mind, I think of those as "imports of elements and functions into a namespace". A declaration like the one above could correlate to the following pseudocode:
import * as xsl from "http://www.w3.org/1999/XSL/Transform";
import * as atom from "http://www.w3.org/2005/Atom";
The general idea is then to output HTML and view it in the
browser. This can be done by setting the xsl:output
method to
HTML.
<xsl:output
method="html"
version="1.0"
encoding="UTF-8"
indent="yes"
/>
To actually create a transformation template, I need to match an element from
my feed.xml
file. The root element is the
feed
element, and as it is under the Atom namespace, I need to
add the atom:
prefix to the element I want to select.
<xsl:template match="atom:feed">
<!-- Here comes the HTML --->
</xsl:template>
Styling XML
To properly set things up, I create a feed.xsl
file referenced by
my feed.
<!-- /feed.xml -->
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet
href="/feed.xsl"
type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<!-- ... -->
</feed>
The important part here is:
<?xml-stylesheet
href="/feed.xsl"
type="text/xsl"?>
The base of the /feed.xsl
looks as follows, setting up the
xsl
and atom
namespaces and enabling
HTML output.
<!-- /feed.xsl -->
<?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="atom:feed">
<!-- nothing here, yet -->
</xsl:template>
</xsl:stylesheet>
The content that goes into xsl:template
will be
HTML. The root element being html
, having a
head
and body
, and everything else an
HTML document needs.
I use the same base HTML structure I use everywhere for my website, including a link to my CSS stylesheet, giving the feed the same look and feel as the rest of my site.
<link rel="stylesheet" href="/style.css" />
Some XSLT basics
The big question for sure is: how to access the contents of the feed and iterate over entries?
Accessing values
Value access works through
XPath
with xsl:value-of
. The root of the feed is already selected
through the xsl:template
, so accessing values inside, like the
tittle, does not need to repeat that path*.
<xsl:template match="atom:feed">
<title>
<xsl:value-of select="atom:title" /> Feed
</title>
</xsl:template>
Defining attributes
Let's say I want to read the blog link from the feed and define it on an
a
element. This uses the xsl:attribute
inside the
a
tag with the attribute name href
.
<a title="To my blog!">
<xsl:attribute name="href">
<xsl:value-of
select="atom:link[2]/@href" />
</xsl:attribute>
<xsl:value-of select="atom:title" />
</a>
Inside the xsl:attribute
the actual value needs to be selected
via xsl:value-of
, using XPath again. The path
atom:link[2]/@href
means:
"Select the second ([2]
) link element, read the value of the
href
attribute (@
)". Elements of a path are separated by a forward slash /
.
Multiple xsl:attribute
can be used, as they will all set the
defined attribute on the parent node.
XSL loops
Loops work by using xsl:for-each
, selecting an element that
occurs multiple times. Every post is represented by an
entry
element inside the feed; therefore,
atom:entry
will be selected.
<xsl:for-each select="atom:entry">
<!-- for every entry ... -->
</xsl:for-each>
Conditions
Using xsl:if
conditions can be created via an XPath expression
that can be evaluated to a boolean value, for example, testing if an element
exists.
<xsl:if test="atom:updated">
<!-- If `updated` exists, do this ... -->
</xsl:if>
Full example
Of course, there is so much more XSLT can do that I cannot cover here (it is a Turing-complete language after all), but those mentioned elements are all I needed for my feed. The full XSLT stylesheet can be found on GitHub.
Having implemented the stylesheet for my feed, accessing the XML file directly in the browser looks way more appealing now, just like the rest of my website, thanks to reusing the existing CSS file.

Bonus: Pretty sitemap
While I was at it, I also added a base style for my XML sitemap. Here is a shortened version of it, showing all the important parts. A full version of the XSLT file can again be found on GitHub.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:sm="http://www.sitemaps.org/schemas/sitemap/0.9">
<xsl:output
method="html"
version="1.0"
encoding="UTF-8"
indent="yes"
/>
<xsl:template match="sm:urlset">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Martin Fieber's Sitemap</title>
<meta
name="viewport"
content="initial-scale=1.0, width=device-width" />
<link
rel="icon"
href="/favicon.ico"
sizes="any" />
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<div class="content">
<header>
<h1>
<a
href="/"
title="Take my back to start!">
Martin Helmut Fieber
</a>
</h1>
</header>
<main>
<h2>Links</h2>
<ul>
<xsl:for-each select="sm:url">
<li>
<a>
<xsl:attribute
name="href">
<xsl:value-of
select="sm:loc" />
</xsl:attribute>
<xsl:attribute
name="title">
Updated:
<xsl:value-of
select="sm:lastmod" />
</xsl:attribute>
<xsl:value-of
select="sm:loc" />
</a>
</li>
</xsl:for-each>
</ul>
</main>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Epilogue
Feeds are awesome, and if you can, you should consider adding one to your site if you have one. And if you're not already using feeds, consider this as well. Getting the content you really like directly from the source, through a service or app you want to use, really changed the quality of the content I consume.
I certainly don't want to miss feeds anymore. Until then 👋🏻