Skip to content

Link previews when syndicating posts that contain links #879

Description

@rmdes

Is your feature request related to a problem?

When a post containing a link is syndicated, what the reader sees varies by platform, and the syndicators don’t currently agree on which link matters.

Two related problems:

1. Bluesky posts show bare links. Upstream builds app.bsky.embed.record, app.bsky.embed.images and app.bsky.embed.recordWithMedia, but never app.bsky.embed.external. Bluesky doesn’t unfurl links itself — without that embed, a syndicated post linking to an article shows the URL as plain text, where the same post on Mastodon gets a preview card. LinkedIn has the same requirement: article posts need an explicit card with a thumbnail.

2. There’s no agreed answer to “which URL represents this post”. Both syndicator-mastodon and syndicator-bluesky currently surface the last link in the content:

const lastHref = hrefs.length > 0 ? hrefs.at(-1)[1] : false;

In a plug-in I’ve been running I ended up with the opposite rule — the first link, on the reasoning that people put the link they’re writing about first and add references afterwards — plus a priority order that looks at post type before content at all:

like-of → bookmark-of → in-reply-to → first external link in content

with own-domain links excluded, so linking to your own bookmark post doesn’t take the card away from the thing being bookmarked.

I’m not claiming that ordering is right, only that it’s a decision currently made three different ways, and it becomes visible as soon as a platform renders a card.

Describe the solution you’d like

Agree one rule for which URL represents a syndicated post, then let each syndicator apply it in whatever way its platform needs — Mastodon by which URL ends up in the text, Bluesky via embed.external, LinkedIn via its article card.

A shared helper would probably help here, since fetching a page’s Open Graph metadata is currently implemented separately in more than one syndicator, and it’s the same work each time.

One question worth settling explicitly, because it has no obvious answer: when includePermalink is enabled, should the card be the external link or the author’s own post? Showing the external link matches what the post is about; showing your own post drives traffic back to your site. Both are reasonable, and it may want to be an option rather than a fixed rule.

Describe alternatives you’ve considered

  • Leave it per-syndicator. Simplest, but the divergence above is the result, and it’s invisible until someone compares the same post across two platforms.
  • Construct the image URL directly for own-domain posts (e.g. /og/<slug>.png). I did this originally and would advise against it: it guesses a path from a URL, which breaks on trailing slashes and permalink changes, and it’s unnecessary — a site that generates preview images already advertises them via og:image, so reading the page gets the same result without the guessing.

Additional context

Happy to open a PR for the Bluesky embed.external part once there’s a view on the URL rule, and to help with the shared helper if that’s the direction. I’d rather agree the behaviour first than implement one answer and have it be the wrong one.

Separately, and probably its own issue: there’s a case for Indiekit generating a default Open Graph card for posts that have no image — built from the h-card and the post title — so that a site’s own links unfurl attractively everywhere, not just on the platforms Indiekit syndicates to. Mentioning it only so it isn’t confused with the above, which is about rendering other people’s cards.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions