jekyll-webmentions-static

Gem Version Tests

Fetch and render Webmention.io mentions as static HTML at Jekyll build time. No JavaScript, no runtime API calls — pure static output.


How it works

During jekyll build, the plugin:

  1. Fetches all webmentions for your domain from the Webmention.io API.
  2. Groups them by target URL and stores them in site.data["webmentions"].
  3. Injects matching mentions into each post's page.webmentions array.
  4. Caches the API response locally so repeated builds don't hammer the API.

Installation

Add to your site's Gemfile:

gem "jekyll-webmentions-static"

And to _config.yml:

plugins:
  - jekyll-webmentions-static

Then run:

bundle install

Configuration

Add to _config.yml:

webmentions:
  token: YOUR_TOKEN        # or use WEBMENTION_TOKEN env var (preferred for CI/CD)
  cache: true              # default: true — cache API responses locally
  cache_ttl: 3600          # default: 3600 — seconds before the cache is considered stale

Getting a token

  1. Go to webmention.io and sign in with your domain (using GitHub, email rel=me, or another IndieAuth provider).
  2. Once authenticated, your API token is shown in your settings.
  3. Add the token to your _config.yml or (strongly preferred) set it as an environment variable:
export WEBMENTION_TOKEN=your-token-here

The environment variable always takes precedence over the config file value. This keeps secrets out of version control.

Webmention.io needs to know your site is using it. Add these two tags to your HTML <head>:

<link rel="webmention" href="https://webmention.io/yourdomain.com/webmention" />
<link rel="pingback" href="https://webmention.io/xmlrpc" />

Usage in templates

Option 1 — Liquid tag (built-in template)

Place {% webmentions %} anywhere in a post or page layout to render a minimal, accessible webmentions section:

{% webmentions %}

This renders likes, reposts, and bookmarks as counts, and replies as individual cards with author name, avatar, and content.

Option 2 — Manual template with page.webmentions

For full control over markup, iterate over page.webmentions directly:

{% if page.webmentions %}
  <section class="webmentions">
    {% assign likes   = page.webmentions | where: "type", "like-of" %}
    {% assign replies = page.webmentions | where: "type", "in-reply-to" %}

    {% if likes.size > 0 %}
      <p>{{ likes.size }} like{{ likes.size | plural: "", "s" }}</p>
    {% endif %}

    {% for mention in replies %}
      <article>
        <strong>{{ mention.author.name }}</strong>
        {% if mention.content.text %}
          <p>{{ mention.content.text }}</p>
        {% endif %}
        <a href="{{ mention.url }}">{{ mention.published | date: "%B %-d, %Y" }}</a>
      </article>
    {% endfor %}
  </section>
{% endif %}

Accessing all webmentions in any template

The full hash is available at site.data.webmentions:

{% assign mentions = site.data.webmentions[page.url | prepend: site.url] %}

Caching

The plugin writes fetched data to _webmentions_cache.json in your site source directory. Subsequent builds within the TTL window (default: 1 hour) read from this file instead of calling the API.

Add the cache file to your .gitignore:

_webmentions_cache.json

To force a fresh fetch, delete the cache file or set cache: false in your config.


GitHub Actions setup

Store your token as a repository secret named WEBMENTION_TOKEN, then expose it in your workflow:

- name: Build Jekyll site
  run: bundle exec jekyll build
  env:
    WEBMENTION_TOKEN: ${{ secrets.WEBMENTION_TOKEN }}

Because the cache file is in .gitignore, each CI build fetches fresh data. To cache across CI runs, add _webmentions_cache.json to your Actions cache key. Without CI caching you'll make one API call per build, which is fine for most sites.

Keeping mentions fresh with a scheduled rebuild

Webmentions only update when your site is rebuilt. If your site doesn't publish frequently, add a schedule trigger to your GitHub Pages workflow so mentions stay current automatically:

on:
  push:
    branches: [main]
  schedule:
    - cron: '0 */6 * * *'   # rebuild every 6 hours

Adjust the cron interval to taste — daily (0 0 * * *) is reasonable for most sites. Each scheduled run fetches any new mentions from Webmention.io and publishes the updated site.


Webmention types

type Meaning
like-of Someone liked your post
repost-of Someone reposted / boosted it
in-reply-to A reply with content
bookmark-of Someone bookmarked it
mention-of A generic link mention

Styling

The {% webmentions %} tag outputs unstyled semantic HTML. All elements use BEM class names:

/* Outer section */
.webmentions { }

/* Reaction count pills */
.webmentions__counts { }
.webmentions__likes { }
.webmentions__reposts { }
.webmentions__bookmarks { }

/* Replies section */
.webmentions__replies { }
.webmentions__replies-heading { }

/* Individual reply card */
.webmention__reply { }
.webmention__author { }
.webmention__avatar { }        /* circular avatar img */
.webmention__author-name { }   /* linked name */
.webmention__date { }
.webmention__content { }

The count pills include a title attribute (e.g. title="2 likes") for native browser tooltip text on hover.


Requirements

  • Ruby >= 2.7
  • Jekyll >= 4.0
  • No additional gems required — uses Ruby's built-in net/http

License

MIT © 2025 Jason Chance