These documents are composed of blocks (structural content elements) which contain spans (text segments with rich formatting).
"Hello world, this is bold and this is a link."
{
"$type": "com.example.block#text",
"spans": [
{ "text": "Hello world, this is " },
{ "text": "bold", "bold": true },
{ "text": " and this is a " },
{ "text": "link", "features": [{ "$type": "com.example.span#link", "uri": "https://example.com" }] },
{ "text": "." }
]
}"To be or not to be, that is the question."
[
{
"$type": "com.example.block#header",
"level": 2,
"spans": [
{ "text": "Introduction" }
]
},
{
"$type": "com.example.block#blockquote",
"spans": [
{ "text": "To be or " },
{ "text": "not to be", "bold": true, "italic": true },
{ "text": ", that is the question." }
]
}
]
- Use
getRecord()to fetch data- See @alice's guide for details
{
"$type": "com.example.block#list",
"children": [
{
"content": {
"$type": "com.example.block#text",
"spans": [
{ "text": "Use " },
{ "text": "getRecord()", "code": true },
{ "text": " to fetch data" }
]
}
},
{
"content": {
"$type": "com.example.block#text",
"spans": [
{ "text": "See " },
{ "text": "@alice", "features": [{ "$type": "com.example.span#mention", "did": "did:plc:alice123" }] },
{ "text": "'s guide for details" }
]
}
}
]
}A span is a segment of rich text with its own text content and optional features. Blocks that contain rich text use an array of spans — each span carries its text and the features that apply to the entire span. No byte-slice indexing is needed; formatting boundaries are expressed by splitting text into separate spans.
| Field | Type | Required | Description |
|---|---|---|---|
text |
string | yes | The text content of this span |
bold |
boolean | no | Bold text |
italic |
boolean | no | Italic text |
underline |
boolean | no | Underlined text |
strike |
boolean | no | Strikethrough text |
code |
boolean | no | Inline code |
highlight |
boolean | no | Highlighted text |
features |
array of feature union (#link, #mention, #bold, #italic, #underline, #strikethrough, #code, #highlight) |
no | Additional features applied to this span's text |
Features are rich text annotations that can be applied to a span.
| Feature | ID | Properties | Description |
|---|---|---|---|
| Bold | #bold |
(none) | Bold text |
| Italic | #italic |
(none) | Italic text |
| Underline | #underline |
(none) | Underlined text |
| Strikethrough | #strikethrough |
(none) | Strikethrough text |
| Code | #code |
(none) | Inline code |
| Highlight | #highlight |
(none) | Highlighted text |
| Link | #link |
uri (string, required) |
Hyperlink. The display text may be simplified but the facet URI should be the complete URL. |
| Mention | #mention |
did (string, format: did, required) |
Mention of a DID identity |
A paragraph of rich text.
| Field | Type | Required | Description |
|---|---|---|---|
spans |
array of com.example.span |
yes | The rich text content |
textSize |
string enum: "default", "small", "large" |
no | Text size variant |
A heading element (h1-h6).
| Field | Type | Required | Description |
|---|---|---|---|
spans |
array of com.example.span |
yes | The heading text |
level |
integer (1-6) | no | Heading level |
id |
string, optional | Identifier string, used for linking to a specific heading |
A block quotation with rich text.
| Field | Type | Required | Description |
|---|---|---|---|
spans |
array of com.example.span |
yes | The quoted text |
An embedded image with required aspect ratio.
| Field | Type | Required | Description |
|---|---|---|---|
image |
blob (accept: image/*, max 1MB) |
yes | The image data |
aspectRatio |
object: { width: integer, height: integer } |
yes | Width and height for layout |
alt |
string | no | Alt text for accessibility |
A code block with optional syntax highlighting.
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | yes | The code content |
language |
string | no | Programming language identifier |
syntaxHighlightingTheme |
string | no | Theme for syntax highlighting |
A list. Each list item contains a content block which can be a nested list.
| Field | Type | Required | Description |
|---|---|---|---|
children |
array of union: #text | #header | #image | #list |
yes | The list items |
style |
string numbers or bullets |
no | What kind of list |
A clickable button/link.
| Field | Type | Required | Description |
|---|---|---|---|
text |
string | yes | Button label |
url |
string (format: uri) | yes | Destination URL |
A website embed/link card with optional preview.
| Field | Type | Required | Description |
|---|---|---|---|
src |
string (format: uri) | yes | The website URL |
title |
string | no | Page title |
description |
string | no | Page description |
previewImage |
blob (accept: image/*, max 1MB) |
no | Preview/OG image |
An embedded atproto object.
| Field | Type | Required | Description |
|---|---|---|---|
ref |
com.atproto.repo.strongRef |
yes | Reference to the record (URI + CID) |
An embedded atproto actor.
| Field | Type | Required | Description |
|---|---|---|---|
did |
string |
yes | Reference to the user (DID) |
An embedded iframe.
| Field | Type | Required | Description |
|---|---|---|---|
url |
string (format: uri) | yes | The iframe source URL |
height |
integer (16-1600) | no | Height in pixels |
A LaTeX math block.
| Field | Type | Required | Description |
|---|---|---|---|
tex |
string | yes | LaTeX source |
A horizontal divider/separator. Has no properties.
@pfrazee feel free to take a look at my implementation of this, as part of a Notion on ATproto that I'm making (definitely just WIP atm). TLDR: here is a document in the format that I made
I work on a rich text editor: https://github.com/TypeCellOS/BlockNote which already had a very similar JSON format for describing documents. So, I was happy to se that you had a very similar approach here. I've made a simple conversion between the BlockNote format & these lexicons.
Meant to chat with you about this at ATmosphereConf, but you were constantly swarmed, especially after the release of attie. Anyway, would be happy to chat anytime about this, or if anyone else is interested in this.