A proof-of-concept exploring the idea of rendering ATProto records as Slack Block Kit embeds. See the Lexicon Embeds discussion on ATProto Discourse for context.
- Transforms atproto records from known schemas into Block Kit JSON
- Fetches live "hydrated" data from APIs
- Uses Handlebars templates (editable in the UI)
- Renders using
slack-blocks-to-jsx
- Rich enough for basic embeds: text, images, author context, action buttons
- Works outside web (Slack, CLI tools, native apps)
- Limitations:
- Buttons only support emoji text, not custom icons (💬 ❤️ instead of proper icons)
- mrkdwn is basic (no headings, code blocks)
- Limited layout options (no side-by-side columns)
You can't just template the raw ATProto record. An endpoint returning Block Kit would need to:
- Hydrate records (resolve handles → display names, fetch avatars, get engagement counts)
- Resolve blobs to CDN URLs
- Process text (handle facets for mentions/links, clean/convert markdown)
- Truncate long content appropriately for embed previews
- Process Facets convert to Blockit/markdown syntax
- Recurse records can embed other records, which themselves need to be hydrated
This means it's more like an AppView than a simple template transform. Each lexicon needs custom logic, not just a different template.
- Doesn't deal with facets
- Limited support for embeds (image, video, external, record)
- Handlebars + JSON is fragile (trailing comma handling, escaping special characters)
- Template errors produce invalid JSON that's hard to debug
- Renderer bugs:
slack-blocks-to-jsxhas a bug where mrkdwn links (<url|text>) render as literal[text](url)instead of anchor tags, so we had to strip links from Weaver previews
- React + TypeScript (Vite)
- slack-blocks-to-jsx for rendering
- Handlebars.js for templating
npm install
npm run dev