Complete Guide to AFFiNE Block Types: Every Built-in Block Explained

AFFiNE's editor supports over 20 built-in block types identified by flavour strings prefixed with affine:, ranging from basic paragraphs and lists to complex embeds, databases, and whiteboard surfaces.

AFFiNE block types form the foundation of the editor's content architecture, built on the open-source BlockSuite framework maintained in the toeverything/AFFiNE repository. Every piece of content—from simple text to embedded Figma designs—is stored as a typed block with a unique flavour identifier. Understanding these block types is essential for developers extending AFFiNE or automating document creation via the BlockSuite API.

Understanding AFFiNE's Block Architecture

The Flavour System

Blocks in AFFiNE are identified by a flavour string prefixed with affine:. This system allows the editor to distinguish between different content types and apply appropriate rendering, serialization, and interaction logic. The flavour serves as the primary key when creating blocks programmatically via doc.addBlock(flavour, props, parentId).

Block Hierarchy and Nesting

AFFiNE documents follow a strict hierarchical structure defined in blocksuite/affine/shared/src/types/index.ts:

  • affine:page serves as the root container for every document
  • affine:note acts as the primary content container within pages
  • Content blocks (paragraphs, lists, images) nest inside notes
  • Edgeless mode uses affine:surface as the canvas root

Complete Catalogue of AFFiNE Block Types

Container Blocks

These blocks provide structural organization for documents:

Content Blocks

Fundamental text and media blocks for document creation:

Embed Blocks

Specialized blocks for embedding external content and linked documents:

Edgeless and Canvas Blocks

Blocks specific to whiteboard and Edgeless mode:

Database Blocks

Structured data blocks:

Programmatic Block Creation with BlockSuite

Developers can instantiate any AFFiNE block type via the BlockSuite API using doc.addBlock(). This method accepts the flavour string, properties object, and parent block ID.

Creating Container Structures

Every AFFiNE document requires a hierarchical structure starting with affine:page:

// Create the root page container
const pageId = doc.addBlock('affine:page', {}, null);

// Add a note container for content
const noteId = doc.addBlock('affine:note', {}, pageId);

Source: tests/blocksuite/e2e/zero-width.spec.ts (lines 46, 49)

Adding Text Content

Insert rich-text paragraphs with initial content:

const paragraphId = doc.addBlock(
  'affine:paragraph', 
  { text: 'Hello AFFiNE!' }, 
  noteId
);

Source: tests/blocksuite/e2e/paragraph.spec.ts (line 245)

Inserting Code Blocks

Create syntax-highlighted code blocks with language specification:

const codeProps = {
  language: 'typescript',
  // initial empty text will be replaced by the editor UI
};
doc.addBlock('affine:code', codeProps, noteId);

Source: tests/blocksuite/e2e/code/crud.spec.ts (line 42)

Creating Lists

Instantiate various list styles using the type property:

// Bulleted list
doc.addBlock('affine:list', { type: 'bulleted' }, noteId);

// Numbered list
doc.addBlock('affine:list', { type: 'numbered' }, noteId);

// Todo/checklist
doc.addBlock('affine:list', { type: 'todo' }, noteId);

Source: tests/blocksuite/e2e/list.spec.ts (lines 64, 80)

Embedding External Content

Insert external resources and linked documents:

// GitHub embed
doc.addBlock('affine:embed-github', { url: 'https://gist.github.com/...' }, noteId);

// YouTube video
doc.addBlock('affine:embed-youtube', { url: 'https://youtu.be/...' }, noteId);

// Linked AFFiNE page
doc.addBlock('affine:embed-linked-doc', { pageId: 'some-page-id' }, noteId);

// Synced document (stays updated with source)
doc.addBlock('affine:embed-synced-doc', { pageId: 'source-page-id' }, noteId);

Source: blocksuite/affine/widgets/keyboard-toolbar/src/config.ts (lines 1005, 1015, 1073, 1115)

Adding Whiteboard Surfaces

Create Edgeless mode canvas blocks:

// Surface block for whiteboard/Edgeless mode
const surfaceId = doc.addBlock('affine:surface', {}, pageId);

Source: tests/blocksuite/e2e/edgeless/edgeless-text.spec.ts (line 616)

Key Implementation Files

The block type system is implemented across these critical source files:

Summary

  • AFFiNE's editor supports 20+ built-in block types identified by affine:-prefixed flavour strings, built on the BlockSuite framework.
  • Container blocks (affine:page, affine:note) provide the hierarchical structure for all documents.
  • Content blocks include paragraphs, lists (bulleted/numbered/todo), code blocks with syntax highlighting, dividers, images, and bookmarks.
  • Embed blocks support external integrations including YouTube, Figma, GitHub, generic iframes, and linked/synced AFFiNE documents.
  • Edgeless blocks (affine:surface, affine:surface-ref) enable whiteboard and canvas functionality.
  • Database blocks (affine:database) provide structured table and list views, while affine:todo represents checklist variants.
  • Developers can create blocks programmatically using doc.addBlock(flavour, props, parentId) as demonstrated in the BlockSuite test suite.

Frequently Asked Questions

What is the difference between affine:page and affine:note blocks?

The affine:page block serves as the root container for every AFFiNE document, representing the top-level page entity in the BlockSuite document model. The affine:note block functions as a generic content container that lives inside pages, holding paragraphs, images, and other content blocks. While every document has exactly one page root, it can contain multiple note blocks to create complex document layouts, as shown in tests/blocksuite/e2e/zero-width.spec.ts (lines 46, 49).

How do I create a todo list programmatically in AFFiNE?

Todo lists are created using the affine:list flavour with the type property set to 'todo'. Using the BlockSuite API, call doc.addBlock('affine:list', { type: 'todo' }, parentNoteId) where parentNoteId references the containing note block. This renders as an interactive checklist with toggleable checkboxes, distinct from bulleted or numbered list variants, as demonstrated in tests/blocksuite/e2e/list.spec.ts (line 80).

What is the difference between embed-linked-doc and embed-synced-doc?

The affine:embed-linked-doc block creates a static inline view of another AFFiNE page at the time of insertion, functioning as a snapshot reference to the linked content. The affine:embed-synced-doc block maintains a live synchronization with the source document, automatically updating the embedded content whenever the original page changes. Both are configured in blocksuite/affine/widgets/keyboard-toolbar/src/config.ts at lines 1073 and 1115 respectively.

Can I create custom block types in AFFiNE?

Yes, AFFiNE supports custom block flavours that extend the core affine: namespace or use custom prefixes like custom:affine:surface:bookmark. These are registered through the BlockSuite schema system and can be added to the toolbar via packages/frontend/core/src/blocksuite/view-extensions/editor-config/toolbar/index.ts. Custom blocks require implementing the corresponding view, model, and service classes following the BlockSuite block definition pattern established in the blocksuite/affine/blocks/ directory structure.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →