DEV Community

Cover image for Setting up Webmentions on my Static Site ๐Ÿ’ฌ
Alex Hyett
Alex Hyett

Posted on • Originally published at alexhyett.com on

Setting up Webmentions on my Static Site ๐Ÿ’ฌ

I have been getting back into blogging recently. I know I write this newsletter every 2 weeks, but I try and keep to a set number of topics as I know my audience is mostly developers.

My blog gives me chance to write about whatever is on my mind, without worrying about it being relevant to my audience.

When I first started "surfing the web" it was all about personal websites and going from one person's blog to another via links and blogrolls. I miss that style of the internet. You know, before it was consolidated into a handful of tech giants.

That internet is still there alive and well, you just need to look a little harder for it. The best way I have found to stumble onto new blogs is to use these:

  • Kagi Small Web - Keep clicking Next Post and it will take you to random websites.
  • Bubbles.town - This is a fairly new addition. It is like HackerNews for personal blogs.

Of course it is all very well just writing into the void but what made social media so popular is the social aspect. Being able to comment on posts, like them and share them.

The IndieWeb has this too and you can like, comment, and reply to posts in the form of webmentions.

How Do Webmentions Work? #

For webmentions to work you need to have a sender and a receiver. The receiver in this case is your website, then sender is someone who wants to like or comment on your post.

To let other people know that you accept webmentions, you have to have to add a link to your webmention endpoin to your website head.

<link rel="webmention" href="{your_webmention_url}">
Enter fullscreen mode Exit fullscreen mode

People can then send you webmentions using an HTTP request to your webmention URL:

curl -X POST {your_webmention_url} \
  -d "source={a_website_that_is_mentioning_you}" \
  -d "target={a_post_on_your_website}"
Enter fullscreen mode Exit fullscreen mode

Of course all of that seems very manual and how do you even get a webmention URL?

Receiving Webmentions on Your Site #

You can host your own webmention server yourself but let's save that for another post.

The easiest way is to go to Webmention.io and type in your website. This is a great free service hosted by Aaron Parecki. It will then give you the option to sign in via an email address or GitHub if you mention your profile on your website.

Once signed in you will get your webmention URL which you can put as a link in your header. It will likely just include your website name as your username.

Mine for example is this:

<link rel="webmention" href="https://webmention.io/www.alexhyett.com/webmention">
Enter fullscreen mode Exit fullscreen mode

This handles receiving webmentions from other peoples websites but what about people liking posts when you mention your blog post on Mastodon or Bluesky?

This is where Bridgy comes in. You can log in with your Mastodon account and it will monitor your posts for mentions of your website and send them in the form of webmentions.

Bridgy even has an option to monitor your RSS feed and automatically post to Mastodon for you. I don't use that as I have my own setup but it is a nice option.

Now you just need to display the webmentions somewhere on your site. You can do this by calling the webmention.io API. You will need to generate a token on their website which you can then use to receive your webmentions. Make sure to keep your API token secret.

As I am on Eleventy, I used this brilliant example on Brennan Kennith Brown website as my base and adjusted the way the webmentions are displayed to suit my site. Each time I build my site (with a 6h cache) it pulls down my webmentions. They are then formatted and displayed on my site.

This is how I render my likes and reposts for example with Tailwind CSS:

{% set interactions = mentions | webmentionsByTypes(['like-of', 'repost-of']) | sortWebmentionsByReceived %}
{% set hiddenInteractions = interactions.length - 51 %}
{% if interactions.length > 0 %}
<div>
  <h3>Likes & Reposts</h3>
  <div class="flex flex-wrap items-center gap-2">
    {% for interaction in interactions | head(51) %}
    <a href="{{ interaction.author.url if interaction.author.url else interaction.url }}"
       title="{{ interaction.author.name | unsupportedEmoji }}"
       target="_blank"
       rel="noopener"
       class="relative inline-block no-underline!">
      {% if interaction.author.photo and interaction.author.photo != '' %}
      <img src="{{ interaction.author.photo }}"
           alt="{{ interaction.author.name | unsupportedEmoji }}"
           loading="lazy"
           class="rounded-full w-12 h-12">
      {% else %}
      <span class="flex h-12 w-12 items-center justify-center rounded-full bg-tertiary font-mono font-bold text-(--header-color)">
        {{ interaction.author.name | unsupportedEmoji | truncate(1) }}
      </span>
      {% endif %}
      {% if interaction["wm-property"] == 'like-of' %}
      <span class="absolute -right-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full border-2 border-(--bg-color) bg-(--secondary-color) text-[11px] text-[#e5484d]" aria-label="Like">
        <i class="fa-solid fa-heart"></i>
      </span>
      {% else %}
      <span class="absolute -right-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full border-2 border-(--bg-color) bg-(--secondary-color) text-[11px] text-(--accent-color-alt)" aria-label="Repost">
        <i class="fa-solid fa-retweet"></i>
      </span>
      {% endif %}
    </a>
    {% endfor %}
    {% if hiddenInteractions > 0 %}
    <span class="font-mono text-sm text-(--text-color-light)">and {{ hiddenInteractions }} more</span>
    {% endif %}
  </div>
</div>
{% endif %}
Enter fullscreen mode Exit fullscreen mode

The unsupportedEmoji filter I added as custom Mastodon icons where showing up as ????. This is just a simple filter to remove them:

eleventyConfig.addFilter("unsupportedEmoji", (text) => {
    return text.replace("????", "");
});
Enter fullscreen mode Exit fullscreen mode

I created a TIL page to test out my webmentions and the Fediverse community were great at giving me examples to test: Setting up webmentions

One part I haven't automated yet is my "Want to interact with this post?" message. I show this on my website.

A screenshot of my discussions section on my website which says

At the moment I have to manually copy the URL for the Fediverse post into my frontmatter field which then gets shown here. So the workflow is:

  1. Publish post
  2. Automated job mentions the post on Mastodon.
  3. I then copy the Mastodon post URL into the frontmatter of the blog post.
  4. Then update my site again.

I need to put add to my workflow to store the share URL to a database, then check for new additions when I build my site and have a script that updates the frontmatter.

Sending Webmentions to Others #

If you have managed to do part one then you have just become an amazing listener but it's not much of a conversation because you are not saying anything back!

If you have had your website for a while like me then chances are you have been saying nothing back for a while. Time to change that.

I wrote a script to go through all of my markdown files on my website and then write all the external URLs to a CSV file. export-external-links.js

I then wrote a separate script that checks the mentioned website to see if they accept webmentions and then sends a request to their webmention endpoint. process-webmentions.js

I would say out of all the links on my website a good 80% of them didn't accept webmentions so it is mostly restricted to indie bloggers who still use them.

That handles all the past requests. For sending new webmentions for posts I have published, I use n8n to monitor my RSS feed, pick out all the external links and then check for the existence of webmentions. Here is the n8n workflow if you wanted to adapt it for your own needs: Send Webmentions.json. I love using n8n these sort of workflows as it makes it really easy to test the flow and debug it.

What's Next #

I have everything working now with replies and likes from Mastodon. I recreated my Bluesky account recently and I am just going to use it to share my posts so I can give people another way to comment. All of my normal social media usage will still be on Mastodon.


โค๏ธ Picks of the Week #

๐Ÿค– AI #

do we NEED generative AI? - We do have to ask ourselves, what do we really gain by using AI? Is the quality better than if you did it yourself? If it is then maybe you should invest more time in your own skills rather than using AI.

Cheap to write, expensive to own - Yes you can write code quicker but you end up with something that is possibly buggy, that you don't understand and you didn't enjoy making. There is still a place for AI in automating one-off jobs with throwaway scripts but if you the code is going to last make sure you read it properly.

Cleaning up after AI rockstar developers - I think this is a really good analogy. Sure there are some rockstar developers out there but most just generate unreadable spaghetti code. This is exactly what AI does but at a faster and much wider scale.

Lines of code got a better publicist - Code is a liability. Repeat that, code is a liability. The more code there is, the harder it is to maintain.

Claude Fable 5: mid-tier results on coding tasks - Despite being banned for being too dangerous it wasn't even that good. Anthropic really shot themselves in the foot with this marketing campaign.

AI-sphyxiation - We probably need another word as well for products that have been slowly ruined by AI code contributions.

๐Ÿ“ Blogging #

Yes, Buy Them a Coffee: Support and Mutual Aid on the IndieWeb - This all started with this post "No, I Won't Buy You A Coffee" and let's just say the IndieWeb didn't like it. Buy me a coffee is essentially a tip jar, it isn't the same as putting up a paywall and it isn't an advert either. I agree with Brennan on this. We have no problem signing up for subscriptions to billionaire corporations but when a human kindly asks you to support them if you like their work then we really should.

๐Ÿ’ป Programming #

Integrating standard.site with My Hugo Blog - Once I get my Bluesky sharing sorted I will have a look at setting this up too. I am not sure why websites need to come up with even more things we need to implement in order for our sites to appear nicely on theirs.

๐Ÿข Tech Industry #

Doing nothing at work - This is a great point. A lot of the work that companies were most thankful for came from taking initiative and having the time to implement it. If you are constantly working on tickets there is no time to innovate.

Surprise, pay $1000 - This is some sneaky tactics. I am not sure this would hold up in court.

๐ŸŽฎ Gaming #

Putt.day a daily mini golf game - This is really fun and well done. The one I played on the 2nd July even had a loop in it.

Pac-Man, but you're the ghost - I love this idea. I still suck at Pac-Man though.

๐Ÿ“š Books #

The Cypherpunk Library - A nice collection of public domain books with a cool UI.

๐Ÿง  Miscellaneous #

New method turns ocean water into drinking water, without waste - This is fantastic. I always thought it was crazy that we are surrounded by all this drinking water but we can't drink any of it.

Public Domain Image Archive - If you are looking for something a bit different for your blog images why not try one of these.


๐Ÿ‘จโ€๐Ÿ’ป Latest from me #

As mentioned I have been getting into blogging a lot more recently. These are my recent posts:

๐Ÿ“ Blog Post - Getting back into blogging - I intentionally restrict the topics I write about in this newsletter but it has stopped me from writing about other stuff.

๐Ÿ“ Blog Post - Taking the unconventional path - I quit my job 3 weeks ago...

๐Ÿ“ Blog Post - Focussing on my fitness - I have been working out pretty regularly over the last month and seeing some progress.


๐Ÿ’ฌ Quote of the Week #

How about we forget โ€œmove fast and break thingsโ€ and instead embrace โ€œgo slow and keep things workingโ€?

OK, yes this one was from me this week.

Top comments (0)