Lexicons
Nick The Sick
March 28, 2026
This post is in reaction to @pfrazee.com here: https://bsky.app/profile/pfrazee.com/post/3mfd4onkpkk2o
It is extensible, meaning that it can offer the addition of new block types; and, even swapping out of the schema entirely.
So, given this, there should be a very straightforward mapping from BlockNote’s JSON to a lexicon, so just as a proof of the concept - I made that mapping. This allows a BlockNote document to be represented as a lexicon & convert to and from the format (essentially a 1:1 mapping since they are so similar).
I actually see this lexicon as sort of being the foundation for a new type of rich text editor, which can have community-driven embeds, in a marketplace.
Take a look at this Typepec for BlockNote for more: https://github.com/typespecs/BlockNote/blob/main/lib/main.tsp
In BlockNote, we de-couple the schema from the document content, this allows us to be agnostic of the implementation of our blocks (content types like paragraphs, code-blocks), while still having a regular structure that we can use to understand the document’s content in a consistent way. Let’s look at an example of the schema that this very document relies on: a paragraph block:
interface ParagraphBlock {
id: string;
type: "paragraph";
props: {
backgroundColor: string;
textColor: string;
textAlignment: "left" | "center" | "right" | "justify";
};
content: InlineContent[];
children: Block[];
};
Or, more generically:
type Block = {
id: string;
type: string;
props: Record;
content: InlineContent[] | TableContent | undefined;
children: Block[];
};
type Link = {
type: "link";
content: StyledText[];
href: string;
};
type StyledText = {
type: "text";
text: string;
styles: Styles;
};
type CustomInlineContent = {
type: string;
content: StyledText[] | undefined;
props: Record;
};
type InlineContent = Link | StyledText | CustomInlineContent;
// tables are special cased, for now
type TableContent = {
type: "tableContent";
columnWidths: (number | undefined)[];
headerRows?: number;
headerCols?: number;
rows: {
cells: TableCell[];
}[];
};
type TableCell = {
type: "tableCell";
props: {
backgroundColor: string;
textColor: string;
textAlignment: "left" | "center" | "right" | "justify";
colspan?: number;
rowspan?: number;
};
content: InlineContent[];
};
Too abstract? Let’s see a BlockNote document:
[
{
"id": "502a74dd-55ba-4637-9e78-9e355fc01469",
"type": "paragraph",
"props": {
"backgroundColor": "default",
"textColor": "default",
"textAlignment": "left"
},
"content": [
{
"type": "text",
"text": "Hello world",
"styles": {}
}
],
"children": []
},
{
"id": "721f2779-fc08-4354-99b6-6031c722cdfe",
"type": "heading",
"props": {
"backgroundColor": "default",
"textColor": "default",
"textAlignment": "left",
"level": 1,
"isToggleable": false
},
"content": [
{
"type": "text",
"text": "Heading",
"styles": {}
}
],
"children": []
},
{
"id": "13a9c876-9809-44c9-8b37-ccba066dad10",
"type": "paragraph",
"props": {
"backgroundColor": "default",
"textColor": "default",
"textAlignment": "left"
},
"content": [],
"children": []
}
]
Want to the see the mapping of this very document? Head on over to it on pdsls!
Discussion in the ATmosphere