Bluesky Rich Text Facet Calculator
Type a post to get the facets Bluesky needs — mentions, links and hashtags with the
UTF-8 byte offsets the AT Protocol requires — and a complete
app.bsky.feed.post record you can send as is.
Everything runs locally: what you type never leaves your browser.
Calculate Bluesky facets
70 / 300 graphemes · 71 UTF-16 units · 74 UTF-8 bytes
Preview
👋 Hi @alice.bsky.social! Café notes: example.com and the docs #atproto
| Type | Text | byteStart | byteEnd |
|---|---|---|---|
| mention | @alice.bsky.social | 8 | 26 |
| link | example.com | 41 | 52 |
| link | the docs | 57 | 65 |
| tag | #atproto | 66 | 74 |
DID for @alice.bsky.social: https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?handle=alice.bsky.social
Why the offsets are in bytes
In the example, the mention starts at byte 8, but at
position 6 in a JavaScript string. The difference is the wave emoji: one
character on screen, two UTF-16 code units, four UTF-8 bytes. The AT Protocol defines facet
ranges in UTF-8 bytes of the post text — byteStart inclusive,
byteEnd exclusive — so a client that uses string indices highlights the wrong
characters as soon as a post contains an emoji or an accented letter.
AT Protocol rich text byte offsets, step by step
To compute AT Protocol rich text byte offsets by hand: find the facet in
the text, encode everything before it as UTF-8 and count the bytes — that is
byteStart. Add the UTF-8 length of the facet text for byteEnd.
This calculator does exactly that for every facet, and the table lets you check each range
against the highlighted preview.
A Bluesky post JSON payload generator
The record on the right is a complete Bluesky post JSON payload: pass it as
the record of a com.atproto.repo.createRecord call with collection
app.bsky.feed.post. Replace the placeholder DID in any mention first, and the
timestamp updates to the current time as soon as you edit the text.
Where these rules come from
The facet structure, the byte-offset definition and the mention, link and tag feature types
are from the AT Protocol lexicon app.bsky.richtext.facet; the post record is
app.bsky.feed.post. Mention, link and tag detection follows the patterns
Bluesky’s own clients use.