Getting Footnotes Right in the Feeds

Published: February 7, 2025.

While upgrading my blog from Nuxt Content 2 to Nuxt Content 3, I made some improvements to my web feed. Previously, the footnotes1 that I added to my posts were not working properly in feed readers, and I wanted to fix that.

Specifically, I noticed how footnotes in John Siracusa's Hypercritical feed did work nicely in my feed reader of choice (NetNewsWire), and it has been a great help in fixing my own feed.

Feeds and feed readers

For the small number of people who read my blog, it's also possible to do that using a feed reader. I publish an Atom feed, which is an XML format for web feeds, similar to the RSS feed. Like RSS, Atom is widely supported, but it is newer and has some additional features.

Personally, I use NetNewsWire as a feed reader. There's also others such as Reeder, Readwise Reader, Feedly, or Feedbin, each with their own set of features (and not all of them support footnotes).

Footnotes in feed readers

So, what do I mean with footnotes working nicely in a feed reader?

There are two features that I require:

  • Footnote pop-ups: When tapping a footnote link, the footnote should appear as a small pop-up at the position of the footnote link in the text, and not scroll down to the footnote list.
  • Backlinks: At the bottom of the article, all footnotes should be listed and they should link back to where the footnote links are in the text, and not open the link in a web view.

This table gives an overview of the extent of support for these features in several feed readers:

Footnote pop-upsBacklinks
NetNewsWire
Reeder
Feedbin
Readwise Reader⏳?
Feedly

✅: Supported
❌: Not supported
⏳: On their backlog

Generating the feed

I write my articles in Markdown, and Nuxt Content generates HTML for each of my articles. For the website, that HTML is simply rendered in a web app.

However, the web feed requires all of the articles to be captured in a single XML file, along with some metadata.

So what's in the feed?

  • Some metadata about the feed itself:
    • Author, language, favicon, copyright, last updated date, etc.
  • For each article (called "entry" in Atom):
    • Entry title, publish date, author, description, link to web page, etc.
    • And most importantly, the content of the entry: The 'raw' HTML that is presented in the feed reader app.

Unfortunately, it is currently not trivial to generate an Atom feed using Nuxt Content. Nuxt Content does provide the HTML abstract syntax tree for the entries, which I can process to generate the HTML that goes in the entry's content.

However, the HTML that Nuxt Content provides does not make my footnotes work properly. This is how the HTML is provided (I added some whitespace & comments to make it more readable):

<!-- a footnote link that appears in
     the article's main content -->
<sup>
    <a
        href="#user-content-fn-1"
        aria-describedby="footnote-label"
        data-footnote-ref
        id="user-content-fnref-1"
    >1</a>
</sup>

<!-- other content -->

<section class="footnotes" data-footnotes>
    <h2 class="sr-only" id="footnote-label">
        Footnotes
    </h2>
    <ol>
        <li id="user-content-fn-1">
            This is the footnote.
            <a
                href="#user-content-fnref-1"
                aria-label="Back to reference 1"
                class="data-footnote-backref"
                data-footnote-backref
            >↩</a>
        </li>
        <!-- other footnotes -->
    </ol>
</section>

Now, observing the hrefs, aria-* attributes, and data-* attributes, it is clear that some effort has been put into footnotes by Nuxt Content's Markdown processor.

However, when I load this into NetNewsWire, clicking the link to the footnote takes me to a web browser where the web page is loaded!

And that made me wonder: How do I make it work like Hypercritical's feed does?

It turned out that I needed to change 2 things:

  • Link to the footnotes with #fn:1 (and #fn:2 and so on).
  • Backlink from the footnotes with #fnref:1 (and #fnref:2 and so on).

Doing this transformation while generating the feed (see my implementation) results in the following HTML:

<!-- a footnote link that appears in
     the article's main content -->
<sup>
    <a
        href="#fn:1"
        aria-described-by="footnote-label"
        data-footnote-ref
        id="fnref:1"
    >1</a>
</sup>

<!-- other content -->

<section class="footnotes" data-footnotes>
    <h2 class="sr-only" id="footnote-label">
        Footnotes
    </h2>
    <ol>
        <li id="fn:1">
            This is the footnote.
            <a
                href="#fnref:1"
                aria-label="Back to reference 1"
                class="data-footnote-backref"
                data-footnote-backref
            >↩</a>
        </li>
    </ol>
</section>

And, lo and behold, this makes the footnote behave perfectly in NetNewsWire.

How do feed readers process this?

Just to find out how my feed reader of choice actually processes this, I found the footnote linking logic and the backlink logic in their source code.

It appears like there's multiple conventions, so it's kind of a mess. It would be nice if there'd by a standardised way of doing footnotes.

There are W3C-recommended ARIA roles called doc-footnote and doc-backlink that appear to be intended for this purpose, but they don't seem to be supported in any feed reader app to my knowledge.

Conclusion

This was a bit of a pain to properly set up, but now I'm glad that it works.

Let me know if you have any other insights into this, I would be happy to hear it.

Footnotes

  1. This is an example of such a footnote. It allows the author to add additional information to their content, without cluttering the main text. Notice how the footnote (when viewing them at the bottom of the article) also links back to the position in the main text where it is linked.