Bluesky Profile Card

Steve PurvesMIT LicensePublished Feb 28, 2026
blueskyuiprofile

An AnyWidget ESM module that fetches and renders a Bluesky profile card directly from the AT Protocol public API — no build step, no API key required.

Embed a live, interactive Bluesky profile card anywhere in a MyST Markdown document or Curvenote article. The widget fetches directly from the AT Protocol public API in the browser at render time — no bundler, no API key, no server required.

Installation

Download bluesky-profile-card.js and host it anywhere that can serve static files (a MyST site’s public/ folder, a CDN, GitHub Pages). Then reference it by URL in the any:bundle directive.

Usage

Add a profile card to any MyST document with the any:bundle directive:

:::{any:bundle} https://example.com/bluesky-profile-card.js
{ "actor": "your-handle.bsky.social" }
:::

The directive body is a JSON object. Only actor is required:

FieldTypeDefaultDescription
actorstringBluesky handle (e.g. "opensci.dev") or DID (required)
show_statsbooleantrueDisplay followers / following / post counts
theme"light" | "dark""light"Card colour scheme
link_targetbooleantrueMake the entire card a clickable link to the Bluesky profile

Minimal example

:::{any:bundle} https://example.com/bluesky-profile-card.js
{ "actor": "mystmd.org" }
:::

Dark theme, stats hidden

:::{any:bundle} https://example.com/bluesky-profile-card.js
{ "actor": "opensci.dev", "theme": "dark", "show_stats": false }
:::

Non-clickable card (embed only)

:::{any:bundle} https://example.com/bluesky-profile-card.js
{ "actor": "opensci.dev", "link_target": false }
:::

How it works

The widget is a plain ESM module (type: module). When rendered, it:

  1. Shows an animated skeleton placeholder immediately.

  2. Calls https://api.bsky.app/xrpc/app.bsky.actor.getProfile — a fully public endpoint, no authentication needed.

  3. Replaces the skeleton with the rendered card, or an error state if the handle could not be resolved.

All network calls happen in the reader’s browser. There is no proxy, no backend, and no credentials involved.

Accessibility

When link_target is true the card element receives role="link" and tabindex="0" and responds to the Enter key, making it keyboard-navigable. The “View on Bluesky” footer link is always present and independently focusable regardless of the link_target setting.

Browser support

Any browser with native ES module support (all modern browsers). No polyfills are required.

Source

The widget source lives in dist/bluesky-profile-card.js and has no runtime dependencies. It uses the browser-native fetch API and plain DOM manipulation.

Further reading


License

Copyright © 2026 Purves. This article is distributed under the terms of the MIT License license.