Docs
Editor Basics
Content Structure

Content Structure

If you want to make the most out of BlockNote, it's important to understand how the editor's content is structured.

Blocks

Each BlockNote editor is made up of a list of blocks. A block - like a heading, paragraph, or list item - contains a piece of content and optionally nested blocks:

image

Block Objects

In code, the Block type is used to describe any given block in the editor:

type Block = {
  id: string;
  type: string;
  props: Record<string, boolean | number | string>;
  content: InlineContent[] | TableContent | undefined;
  children: Block[];
};

id: The block's ID. Multiple blocks cannot share a single ID, and a block will keep the same ID from when it's created until it's removed.

type: The block's type, such as a paragraph, heading, or list item. For an overview of built-in block types, see Default Blocks.

props: The block's properties, which is a set of key/value pairs that further specify how the block looks and behaves. Different block types have different props - see Default Blocks for more.

content: The block's rich text content, usually represented as an array of InlineContent objects. This does not include content from any nested blocks. Read on to Inline Content for more on this.

children: Any blocks nested inside the block. The nested blocks are also represented using Block objects.

Editor Content in JSON

The demo below shows how the editor content is represented in code - notice that it's in JSON as an array of Block objects.

import { Block } from "@blocknote/core";
import { BlockNoteView, useCreateBlockNote } from "@blocknote/react";
import "@blocknote/react/style.css";
import { useState } from "react";
 
export default function App() {
  // Stores the editor's contents as an array of Block objects.
  const [blocks, setBlocks] = useState<Block[]>([]);
  // Creates a new editor instance.
  const editor = useCreateBlockNote({
    initialContent: [
      {
        type: "paragraph",
        content: "Welcome to this demo!",
      },
      {
        type: "heading",
        content: "This is a heading block",
      },
      {
        type: "paragraph",
        content: "This is a paragraph block",
      },
      {
        type: "paragraph",
      },
    ],
  });
 
  // Renders the editor instance and its contents, as an array of Block
  // objects, below.
  return (
    <div>
      <BlockNoteView
        editor={editor}
        onChange={() => {
          // Converts the editor's contents to an array of Block objects.
          setBlocks(editor.topLevelBlocks);
        }}
      />
      <p>Document JSON:</p>
      <pre>{JSON.stringify(blocks, null, 2)}</pre>
    </div>
  );
}
 

Inline Content

A block's content is referred to as inline content, and is used to represent rich text.

TODO: Between custom & default inline content types, the shapes on inline content objects are not standardized. e.g. Links have href and no props, custom IC will have props. StyledText can be IC itself, or used in other IC types.

I think we should have smth like this:

// Helper types
type Styles = {
  bold: boolean;
  textColor: string;
  ...
}
 
// Inline Content types
type StyledText = {
  type: "richText";
  props: {
    styles: Styles;
  }
  editable: true;
}
type Link = {
  type: "link";
  props: {
    styles: Styles;
    href: string;
  }
  editable: true;
}
type Mention = {
  type: "mention";
  props: {
    user: string;
  }
  editable: false;
}

This is way cleaner, but means that links cannot have mixed styles. I think we either:

  • Convert links to styles (they're already marks in TipTap)
  • Not do anything bc I don't think having mixed styles on links is ever useful

Inline Content Objects

In code, the InlineContent type is used to describe a piece of inline content:

type InlineContent = {
  type: string;
  props: Record<string, boolean | number | string>;
  editable: boolean;
};

type: The inline content's type, such as a mention or styled text. For an overview of built-in inline content types, see Default Inline Content.

props: The inline content's properties, which are stored in a set of key/value pairs and specify how the inline content looks and behaves. Different inline content types have different props - see Default Inline Content for more.

editable: Whether the inline content contains editable text.

Types of Block Content

Most blocks will use an array of InlineContent objects to describe their content, such as paragraphs, headings and list items. Some blocks, like images, don't contain any rich text content, so their content fields will be undefined.

Tables are also different, as they contain TableContent. Here, each table cell is represented as an array of InlineContent objects:

type TableContent = {
  type: "tableContent";
  rows: {
    cells: InlineContent[][];
  }[];
};

Block Content in JSON

The demo below shows how the block content is represented in code by outputting only the content field of each top level block. As in the previous demo, notice that it's in JSON.

import {
  DefaultInlineContentSchema,
  DefaultStyleSchema,
  InlineContent,
  TableContent,
} from "@blocknote/core";
import { BlockNoteView, useCreateBlockNote } from "@blocknote/react";
import "@blocknote/react/style.css";
import { useState } from "react";
 
export default function App() {
  // Stores the editor's contents as an array of Block objects.
  // TODO: InlineContent and TableContent should have default type args
  const [inlineContent, setInlineContent] = useState<
    (
      | InlineContent<DefaultInlineContentSchema, DefaultStyleSchema>[]
      | TableContent<DefaultInlineContentSchema, DefaultStyleSchema>
      | undefined
    )[]
  >([]);
  // Creates a new editor instance.
  const editor = useCreateBlockNote({
    initialContent: [
      {
        type: "paragraph",
        content: "Welcome to this demo!",
      },
      {
        type: "paragraph",
        content: [
          {
            type: "text",
            text: "Hello ",
            styles: {},
          },
          {
            type: "text",
            text: "there, ",
            styles: {
              bold: true,
            },
          },
          {
            type: "link",
            content: "BlockNote",
            href: "https://www.blocknotejs.org",
          },
        ],
      },
      {
        type: "paragraph",
      },
    ],
  });
 
  // Renders the editor instance and the contents of its blocks below.
  return (
    <div>
      <BlockNoteView
        editor={editor}
        onChange={() => {
          // Converts the editor's contents to an array with each top level
          // block's content.
          setInlineContent(editor.topLevelBlocks.map((block) => block.content));
        }}
      />
      <p>Document JSON:</p>
      <pre>{JSON.stringify(inlineContent, null, 2)}</pre>
    </div>
  );
}