Bluesky Engagement Widget

Client-side comments, likes & reposts from a linked Bluesky post. No auth, no server, no dependencies.

This widget fetches engagement data from the public Bluesky API and renders it inline. It works as a progressive enhancement — if the API is unreachable or the post doesn't exist, the widget simply doesn't render.


Try it

Enter any Bluesky post URL to see its engagement:


Usage

Add a container element with data-bsky-uri and include the script:

<!-- Container: set data-bsky-uri to your Bluesky post -->
<div data-bsky-uri="https://bsky.app/profile/you.bsky.social/post/abc123"></div>

<!-- Widget script (defer recommended) -->
<script type="module" src="/bsky/bsky-engagement.js"></script>

Configuration

All options are set via data- attributes:

<div data-bsky-uri="https://bsky.app/profile/..."
     data-bsky-sort="likes"       <!-- likes | newest | oldest -->
     data-bsky-max-depth="3"      <!-- reply nesting depth -->
     data-bsky-show="all"         <!-- all | comments | stats -->
     data-bsky-theme="auto"       <!-- auto | light | dark -->
></div>

Events

The container emits custom events you can listen to:

const el = document.querySelector('[data-bsky-uri]');
el.addEventListener('bsky:loaded', (e) => {
  console.log('Post:', e.detail.post);
  console.log('Replies:', e.detail.replies.length);
  console.log('Likes:', e.detail.likes.length);
});
el.addEventListener('bsky:error', (e) => {
  console.warn('Widget failed:', e.detail.error);
});

Styling

Override any CSS custom property on .bsky-engagement:

.bsky-engagement {
  --bsky-font: 'Your Font', sans-serif;
  --bsky-text: #333;
  --bsky-accent: #0085ff;
  --bsky-heart: #ec4899;
  --bsky-repost: #22c55e;
  --bsky-border: #eee;
  --bsky-radius: 8px;
}