jekyll-webmentions-static
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:
- Fetches all webmentions for your domain from the Webmention.io API.
- Groups them by target URL and stores them in
site.data["webmentions"]. - Injects matching mentions into each post's
page.webmentionsarray. - 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
- Go to webmention.io and sign in with your domain (using GitHub, email
rel=me, or another IndieAuth provider). - Once authenticated, your API token is shown in your settings.
- Add the token to your
_config.ymlor (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.
Add the rel="webmention" link to your <head>
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