Webmentions in Eleventy

In the interest of continuing to repeat myself I'm writing, once again, about adding webmentions to a blog.[1] To quote myself[2]:

To kick this off you'll need to log in and establish an account with webmention.io and Bridgy. The former provides you with a pair of meta tags that collect webmentions, the latter connects your site to social media[1:1]

Once you've added the appropriate tags from webmention.io, connected your desired accounts to Bridgy and received some mentions on these sites, you should be able to access said mentions via their API.

I'm fetching data from webmention.io at build time in /src/_data/webmentions.js:

const EleventyFetch = require('@11ty/eleventy-fetch')

module.exports = async function () {
    const KEY_CORYD = process.env.API_KEY_WEBMENTIONS_CORYD_DEV
    const url = `https://webmention.io/api/mentions.jf2?token=${KEY_CORYD}&per-page=1000`
    const res = EleventyFetch(url, {
        duration: '1h',
        type: 'json',
    })
    const webmentions = await res
    return {
        mentions: webmentions.children,
    }
}

I have cache duration set to 1h and a scheduled build operating on approximately the same schedule that's handled by a GitHub action leveraging Vercel's CLI:

name: Scheduled Vercel build
env:
    VERCEL_ORG_ID: $
    VERCEL_PROJECT_ID: $
on:
    schedule:
        - cron: '0 * * * *'
jobs:
    cron:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v2
            - name: Install Vercel CLI
              run: npm install --global vercel@latest
            - name: Pull Vercel Environment Information
              run: vercel pull --yes --environment=production --token=$
            - name: Build Project Artifacts
              run: vercel build --prod --token=$
            - name: Deploy Project Artifacts to Vercel
              run: vercel deploy --prebuilt --prod --token=$

When the build runs, it renders any mentions of a given post via a liquid.js template that looks like this:

{% if webmentions %}
    <div class="border-t border-gray-200 mt-12 pt-14 dark:border-gray-700">
    {% assign mentions = webmentions.mentions | webmentionsByUrl: page.url %}
    {% if mentions['repost-of'].size > 0 %}
    <h2 class="text-lg md:text-xl font-black leading-tight dark:text-gray-200">Reposts</h2>
    <div class="flex flex-row items-center mt-4 mb-6">
        <ul class="ml-3 flex flex-row">
        {% for mention in mentions['repost-of'] %}
            <li class="-ml-3 inline">
                <a href={{mention.url}}>
                    <img
                        src={{mention.author.photo}}
                        alt={{mention.author.name}}
                        class="h-14 w-14 rounded-full border-4 border-white dark:border-gray-900 transition-all hover:border-primary-500 dark:hover:border-primary-300"
                        loading="lazy"
                    />
                </a>
            </li>
        {% endfor %}
        </ul>
    </div>
    {% endif %}
    {% if mentions['like-of'].size > 0 %}
    <h2 class="text-lg md:text-xl font-black leading-tight dark:text-gray-200">Likes</h2>
    <div class="flex flex-row items-center mt-4 mb-6">
        <ul class="ml-3 flex flex-row">
        {% for mention in mentions['like-of'] %}
            <li class="-ml-3 inline">
                <a href={{mention.url}}>
                    <img
                        src={{mention.author.photo}}
                        alt={{mention.author.name}}
                        class="h-14 w-14 rounded-full border-4 border-white dark:border-gray-900 transition-all hover:border-primary-500 dark:hover:border-primary-300"
                        loading="lazy"
                    />
                </a>
            </li>
        {% endfor %}
        </ul>
    </div>
    {% endif %}
    {% if mentions['in-reply-to'].size > 0 %}
    <h2 class="text-lg md:text-xl font-black leading-tight dark:text-gray-200">Comments</h2>
    <div class="mt-4 flex flex-col items-center not-prose">
        {% for mention in mentions['in-reply-to'] %}
        <div class="border-bottom flex flex-row items-center border-gray-100 pb-4 w-full">
            <a class="group flex flex-row space-between items-center" href={{mention.url}}>
                <img
                    src={{mention.author.photo}}
                    alt={{mention.author.name}}
                    class="h-14 w-14 rounded-full border-4 border-white dark:border-gray-900 transition-all group-hover:border-primary-500 dark:group-hover:border-primary-300"
                    loading="lazy"
                />
                <div class="ml-3">
                    <p class="text-sm group-hover:text-primary-500 dark:group-hover:text-primary-300">{{mention.content.text}}</p>
                    <p class="mt-1 text-xs group-hover:text-primary-500 dark:group-hover:text-primary-300">{{mention.published | isoDateOnly}}</p>
                </div>
            </a>
        </div>
        {% endfor %}
    </div>
    {% endif %}
    </div>
{% endif %}

This conditionally displays different mention types based on the available data after being passed through the webmentionsByUrl filter which I shamelessly lifted from Robb.

I would like to also send outbound webmentions, but have yet to find a clear way to do so. One option I've seen mentioned is Telegraph, but it's API interface requires a network call per link mentioned (which isn't untenable, but parsing posts for links and making a call for each feels suboptimal)[3].


  1. I've done so in Next.js and entirely in Javascript for weblog.lol. ↩︎ ↩︎

  2. Or, better yet, read Robb's post on the subject. ↩︎

  3. I'm probably missing something here and am happy to be told I'm wrong. ↩︎