Martin Helmut Fieber

Feed at its best

Posted on — Updated

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"
/>
This is the HTML tag for an Atom feed. For RSS the 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.

Screenshot of the raw XML of my feed, as typically presented by browsers when opening an XML file, with a message that there is no associated style information.
Not a particular user-friendly view of the feed.

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";
This is at least how I think about looking at the XSL.

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>
Reading the title from the feed and using it as the HTML document title.

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.

This is how an XML feed can look like? 🔥

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 👋🏻

← Show all blog posts