Files
upstream-affine/blocksuite/docs-site/guide/block-schema.md
upstream-mirror 50f788e799
Some checks failed
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 4s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Analyze (javascript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
snapshot of https://github.com/toeverything/AFFiNE.git (canary, 2026-09-16; blobs > 131072B omitted)
2026-09-16 00:31:07 +05:00

4.2 KiB

Block Schema

In BlockSuite, all blocks should have a schema. The schema of the block describes the data structure of the block.

You can use the defineBlockSchema function to define the schema of the block.

import { defineBlockSchema } from '@blocksuite/store';

export const MyBlockSchema = defineBlockSchema({
  flavour: 'my-block',
  props: internal => ({
    text: internal.Text(),
    level: 0,
  }),
  metadata: {
    version: 1,
    role: 'content',
  },
});

Flavour and Props

Key takeaways for this part:

  • The flavour of the block is a string that uniquely identifies the block. You can think of it as the name of the block.
  • The props of the block are some attributes that the block has. They can be updated by some user actions. And they can be used to render the block. Some typical props are text, level, url, src, etc.
  • You can use most of the primitive types in the props. But you should not use undefined or null in the props.
  • We also support some special types in the props, called internal types. The internal types are used to describe some internal data structures of the block.
  • internal.Text is a special type that represents the text of the block. It represents Y.Text in the Yjs.
  • You can also use arrays and objects in props.

Schema Relations

You can also declare some relations between blocks in the schema.

Role

You should declare a role for every block you create. The role of the block can be 3 values:

  • root: The block is the root of the document. A document can only have one root block.
  • hub: The block is a hub. A hub can have multiple children. The children of it can be either hub or content.
  • content: The leaf block of the document. A content block can only have one parent. Also, it can only have content as its children.

For example:

root
| hub1
| | content1
| | | content2
| hub2
| | hub3
| | | content3
| | content4

Parent and Children

By default, a block will validate its children and parent by its role. You can also pass a parent or children option to the schema to override the default behaviour.

Some examples:


This means the block's children must match the flavour my-leaf.

import { defineBlockSchema } from '@blocksuite/store';

export const MyBlockSchema = defineBlockSchema({
  // ...
  metadata: {
    children: ['my-leaf'],
  },
});

When passing *, it means all blocks that match the rule of role can be used.

export const MyBlockSchema = defineBlockSchema({
  // ...
  metadata: {
    children: ['*'],
  },
});

You can also pass glob patterns:

export const MyBlockSchema = defineBlockSchema({
  // ...
  metadata: {
    children: ['my-data-*'],
  },
});

The glob match feature is powered by minimatch.


This means the block won't accept any children.

export const MyBlockSchema = defineBlockSchema({
  // ...
  metadata: {
    children: [],
  },
});

Schema to Model

The schema of the block is used to generate the model of the block. By default, the model will holds the flavour, props and id of the block.

MyBlockSchema
  -> MyBlockModel-1
  -> MyBlockModel-2
  -> MyBlockModel-3

For example, if we have a schema like this:

import { defineBlockSchema, type Text } from '@blocksuite/store';

export type MyBlockProps = {
  text: Text;
  level: number;
};

export const MyBlockSchema = defineBlockSchema({
  flavour: 'my-block',
  props: (internal): MyBlockProps => ({
    text: internal.Text(),
    level: 0,
  }),
  metadata: {
    version: 1,
    role: 'content',
  },
});

And when the model is created, you can use it like this:

import { type SchemaToModel } from '@blocksuite/store';

function doSomething(model: SchemaToModel<typeof MyBlockSchema>) {
  const id = model.id;
  const flavour = model.flavour;
  const text = model.text;
  const level = model.level;
}

You can also customize the model by extending the BlockModel to provide more methods:

export class MyBlockModel extends BlockModel<MyBlockProps> {
  levelUp() {
    this.level += 1;
  }
}

function doSomething(model: MyBlockModel) {
  model.levelUp();
  const level = model.level;
}