Semi-automated hashtags for syndicated posts
I went out on a limb recently and decided to build a custom collection in Eleventy that aggregates my post tags and link tags (sourced from Matter). These tags then get appended to shared post or link titles when they're syndicated from my site.
I went out on a limb recently and decided to build a custom collection in Eleventy that aggregates my post tags and link tags (sourced from Matter). These tags then get appended to shared post or link titles when they're syndicated from my site.
The collection I built sits in my .eleventy.js
file and looks like this:
eleventyConfig.addCollection('tagMap', (collection) => {
const tags = {}
collection.getAll().forEach((item) => {
if (item.data.collections.posts) {
item.data.collections.posts.forEach((post) => {
const url = post.url.includes('http') ? post.url : `https://coryd.dev${post.url}`
const tagString = [...new Set(post.data.tags.map((tag) => tagAliases[tag.toLowerCase()]))]
.join(' ')
.trim()
if (tagString) tags[url] = tagString
})
}
if (item.data.links) {
item.data.links.forEach((link) => {
const tagString = [...new Set(link.tags.map((tag) => tagAliases[tag.toLowerCase()]))]
.join(' ')
.trim()
if (tagString) tags[link.url] = tagString
})
}
})
return tags
})
This works by iterating through the posts and global link data available from collections.getAll()
and then mapping each tag string to the associated url in a tags
object. The tags
object is returned and made available as tagMap
.
Once the tagMap
is available it's leveraged in a template that renders JSON feeds for my site — if tagMap
is passed into the LiquidJS render tag for the feed it's leveraged to help populate the title.
The template:
{%- assign entries = data | normalizeEntries -%}
{
"version": "https://jsonfeed.org/version/1",
"title": "{{ title }}",
"icon": "https://coryd.dev/static/images/avatar.webp",
"home_page_url": "{{ site.url }}",
"feed_url": "{{ site.url }}{{ permalink }}",
"items": [{% for entry in entries limit: 20 -%}
{
"id": "{{ entry.url | btoa }}",
"title": "{{ entry.title | escape }}",
"url": "{{ entry.url }}",
"content_text": "{{ entry.title | escape }}{% if tagMap %} {{ entry.url | tagLookup: tagMap }}{% endif %} {{ entry.url }}",
"date_published": "{{ entry.date | stringToDate | dateToRfc822 }}"
}{% if not forloop.last %},{% endif %}
{%- endfor %}
]
}
Leveraging the template:
{% render "partials/feeds/json.liquid"
permalink:'/feeds/follow'
title:'Follow • Cory Dransfeldt'
data:follow.posts
updated:follow.posts[0].date_published
site:site
tagMap:collections.tagMap
%}
The exceedingly simple tagLookup
filter:
tagLookup: (url, tagMap) => tagMap[url],
The static part of this whole setup is the tagAliases
object referenced in my custom collection, which is a curated JSON object that maps tags I typically use between my blog and Matter to formatted hashtags to share and, as of writing, looks like this:
{
"11ty": "#Eleventy",
"accessibility": "#Accessibility",
"development": "#WebDev",
"eleventy": "#Eleventy",
"email": "#Email",
"fastmail": "#Email",
"gmail": "#Email",
"javascript": "#JavaScript",
"last.fm": "#Music",
"lastfm": "#Music",
"music": "#Music",
"react": "#JavaScript",
"social media": "#Tech",
"spotify": "#Music",
"tech": "#Tech",
"technology": "#Tech"
}
So, with all of this in place, when my site builds it generates the tagMap
collection, which is passed into my json.liquid
feed template, that then looks up the matching tags via tagLookup
before being syndicated using a GitHub action. The result is the same syndication that was in place before, but with a few additional tags to help with discovery.