Webmention is a W3C recommendation last published on January 12, 2017. And what exactly is a Webmention? Its described as&
[…] a simple way to notify any URL when you mention it on your site. From the receiver’s perspective, it’s a way to request notifications when other sites mention it.
In a nutshell, its a way of letting a website know it has been mentioned somewhere, by someone, in some way. The Webmention spec also describes it as a way for a website to let others know it cited them. What that basically bails down to is that your website is an active social media channel, channeling communication from other channels (e.g. Twitter, Instagram, Mastodon, Facebook, etc.).
How does a site implement Webmentions? In some cases, like WordPress, its as trivial as installing a couple of plugins. Other cases may not be quite so simple, but its still pretty straightforward. In fact, lets do that now!
Heres our plan
Declare an endpoint to receive Webmentions
Process social media interactions to Webmentions
Get those mentions into a website/app
Set the outbound Webmentions
Luckily for us, there are services in place that make things extremely simple. Well, except that third point, but hey, its not so bad and Ill walk through how I did it on my own atila.io site.
My site is a server-side blog that’s pre-rendered and written with NextJS. I have opted to make Webmention requests client-side; therefore, it will work easily in any other React app and with very little refactoring in any other JavaScript application.
Step 1: Declare an endpoint to receive Webmentions
In order to have an endpoint we can use to accept Webmentions, we need to either write the script and add to our own server, or use a service such as Webmention.io (which is what I did).
Webmention.io is free and you only need to confirm ownership over the domain you register. Verification can happen a number of ways. I did it by adding a rel="me" attribute to a link in my website to my social media profiles. It only takes one such link, but I went ahead and did it for all of my accounts.
Verifying this way, we also need to make sure theres a link pointing back to our website in that Twitter profile. Once weve done that, we can head back to Webmention.io and add the URL.
This gives us an endpoint for accepting Webmentions! All we need to do now is wire it up as <link> tags in the <head> of our webpages in order to collect those mentions.
Remember to replace {user} with your Webmention.io username.
Step 2: Process social media interactions into Webmentions
We are ready for the Webmentions to start flowing! But wait, we have a slight problem: nobody actually uses them. I mean, I do, you do, Max B�ck does, Swyx does, and& thats about it. So, now we need to start converting all those juicy social media interactions into Webmentions.
And guess what? Theres an awesome free service for it. Fair warning though: youd better start loving the IndieWeb because were about to get all up in it.
Bridgy connects all our syndicated content and converts them into proper Webmentions so we can consume it. With a SSO, we can get each of our profiles lined up, one by one.
Step 3: Get those mentions into a website/app
Now its our turn to do some heavy lifting. Sure, third-party services can handle all our data, but its still up to us to use it and display it.
Were going to break this up into a few stages. First, well get the number of Webmentions. From there, well fetch the mentions themselves. Then well hook that data up to NextJS (but you don’t have to), and display it.
Get the number of mentions
type TMentionsCountResponse = {
� count: number
� type: {
� � like: number
� � mention: number
� � reply: number
� � repost: number
� }
}
That is an example of an object we get back from the Webmention.io endpoint. I formatted the response a bit to better suit our needs. Ill walk through how I did that in just a bit, but heres the object we will get:
type TMentionsCount = {
� mentions: number
� likes: number
� total: number
}
The request will not fail without it, but the data wont come either. Both Max B�ck and Swyx combine likes with reposts and mentions with replies. In Twitter, they are analogous.
Before getting to the response, please note that the response is paginated, where the endpoint accepts three parameters in the query:
page: the page being requested
per-page: the number of mentions to display on the page
target: the URL where Webmentions are being fetched
Once we hit https://webmention.io/api/mentions and pass the these params, the successful response will be an object with a single key links which is an array of mentions matching the type below:
Were going to work in NextJS for a moment. Its all good if you arent using NextJS or even have a web app. We already have all the data, so those of you not working in NextJS can simply move ahead to Step 4. The rest of us will meet you there.
As of version 9.3.0, NextJS has three different methods for fetching data:
getStaticProps: fetches data on build time
getStaticPaths: specifies dynamic routes to pre-render based on the fetched data
getServerSideProps: fetches data on each request
Now is the moment to decide at which point we will be making the first request for fetching mentions. We can pre-render the data on the server with the first batch of mentions, or we can make the entire thing client-side. I opted to go client-side.
If youre going client-side as well, I recommend using SWR. Its a custom hook built by the Vercel team that provides good caching, error and loading states it and even supports React.Suspense.
Display the Webmention count
Many blogs show the number of comments on a post in two places: at the top of a blog post (like this one) and at the bottom, right above a list of comments. Lets follow that same pattern for Webmentions.
First off, lets create a component for the count:
const MentionsCounter = ({ postUrl }) => {
� const { t } = useTranslation()
� // Setting a default value for `data` because I don't want a loading state
� // otherwise you could set: if(!data) return <div>loading...</div>
� const { data = {}, error } = useSWR(postUrl, getMentionsCount)
(
� if (error) {
� � return <ErrorMessage>{t('common:errorWebmentions')}</ErrorMessage>
� }
(
� // The default values cover the loading state
� const { likes = '-', mentions = '-' } = data
(
� return (
� � <MentionCounter>
� � � <li>
� � � � <Heart title="Likes" />
� � � � <CounterData>{Number.isNaN(likes) ? 0 : likes}</CounterData>
� � � </li>
� � � <li>
� � � � <Comment title="Mentions" />{' '}
� � � � <CounterData>{Number.isNaN(mentions) ? 0 : mentions}</CounterData>
� � � </li>
� � </MentionCounter>
� )
}
Thanks to SWR, even though we are using two instances of the WebmentionsCounter component, only one request is made and they both profit from the same cache.
Feel free to peek at my source code to see whats happening:
Display the mentions
Now that we have placed the component, its time to get all that social juice flowing!
At of the time of this writing, useSWRpages is not documented. Add to that the fact that the webmention.io endpoint doesnt offer collection information on a response (i.e. no offset or total number of pages), I couldnt find a way to use SWR here.
So, my current implementation uses a state to keep the current page stored, another state to handle the mentions array, and useEffect to handle the request. The Load More button is disabled once the last request brings back an empty array.
Thanks to Remy Sharp, handling outbound mentions from one website to others is quite easy and provides an option for each use case or preference possible.
The quickest and easiest way is to head over to Webmention.app, get an API token, and set up a web hook. Now, if you have RSS feed in place, the same thing is just as easy with an IFTT applet, or even a deploy hook.
If you prefer to avoid using yet another third-party service for this feature (which I totally get), Remy has open-sourced a CLI package called wm which can be ran as a postbuild script.
But thats not enough to handle outbound mentions. In order for our mentions to include more than simply the originating URL, we need to add microformats to our information. Microformats are key because its a standardized way for sites to distribute content in a way that Webmention-enabled sites can consume.
At their most basic, microformats are a kind of class-based notations in markup that provide extra semantic meaning to each piece. In the case of a blog post, we will use two kinds of microformats:
h-entry: the post entry
h-card: the author of the post
Most of the required information for h-entry is usually in the header of the page, so the header component may end up looking something like this:
<header class="h-entry">
� <!-- the post date and time -->
� <time datetime="2020-04-22T00:00:00.000Z" class="dt-published">
� � 2020-04-22
� </time>
� <!-- the post title -->
� <h1 class="p-name">
� � Webmentions with NextJS
� </h1>
</header>
And thats it. If youre writing in JSX, remember to replace class with className, that datetime is camelCase (dateTime), and that you can use the new Date('2020-04-22').toISOString() function.
Its pretty similar for h-card. In most cases (like mine), author information is below the article. Heres how my pages footer looks:
Now, whenever we send an outbound mention from this blog post, it will display the full information to whomever is receiving it.
Wrapping up
I hope this post has helped you getting to know more about Webmentions (or even about IndieWeb as a whole), and perhaps even helped you add this feature to your own website or app. If it did, please consider sharing this post to your network. I will be super grateful! 😉