BlockNote DocsFeaturesCustom SchemasLayout Blocks

Layout Blocks

A layout block is a custom block that holds and arranges other blocks, but has no content of its own. Panels, tab sets and column layouts are examples. Its child blocks are inside the layout block, not indented below it, and the layout block draws a frame around them. A layout block can also limit which blocks it holds, for example only tabs in a tab set.

If your block has its own text and also child blocks, for example an alert with a title and a body, use a custom block with a frame. See Layout Blocks and Block Frames for the differences.

Take a look at the demo below, in which we add a custom panel to a BlockNote editor, as well as a custom Slash Menu item to insert it. Each panel can contain paragraphs, headings, lists, or any other blocks in the editor.

For more advanced layout blocks, see the Tabs Block and Stepper Block examples.

Creating a Layout Block

Use the createReactBlockSpec function to create a layout block, just like a Custom Block. For the panel below, we set content to "none" and layoutOnly to true, so the panel's child blocks go inside it:

import { createReactBlockSpec } from "@blocknote/react";

export const createPanel = createReactBlockSpec(
  {
    type: "panel",
    propSchema: {},
    content: "none",
    layoutOnly: true,
  },
  {
    renderFrame: (props) => (
      <div className="panel" ref={props.slotRef} />
    ),
  },
);

Block Config

The block config defines the content and child blocks your layout block can hold:

content: Must be "none" for a layout block. A layout block holds only its child blocks. For a block with its own text and child blocks, see Layout Blocks and Block Frames.

layoutOnly: Set to true to put the block's child blocks inside it. Without it, a block's child blocks are indented below it. A layout block accepts any block by default. You can also restrict it to specific layout block types, as explained in Restricting Children.

propSchema: Defines the layout block's props, just like for other custom blocks. Use these to customize its appearance or behavior.

Block Implementation

renderFrame: Your React component draws the layout block's frame. A layout block has no content of its own, so it has no render, only this frame. Attach slotRef to the element that holds the child blocks. You can add icons, buttons, or other elements around this element. renderFrame takes the same props as for other blocks:

renderFrame: (props) => (
  <div className="panel">
    <span contentEditable={false}>💡</span>
    <div ref={props.slotRef} />
  </div>
),

You can style the component with CSS, just like any other React component:

.panel {
  display: flex;
  gap: 12px;
  padding: 16px;
  border-left: 4px solid #507aff;
  border-radius: 6px;
}

Adding Layout Blocks to the Editor

Add your layout block to a BlockNote schema:

import { BlockNoteSchema } from "@blocknote/core";
import { createPanel } from "./Panel";

const schema = BlockNoteSchema.create().extend({
  blockSpecs: {
    panel: createPanel(),
  },
});

You can then create an editor with this schema, as explained on the Custom Schemas page. Use children to set the blocks inside a panel:

import { useCreateBlockNote } from "@blocknote/react";

const editor = useCreateBlockNote({
  schema,
  initialContent: [
    {
      type: "panel",
      children: [
        { type: "heading", content: "Getting started" },
        { type: "paragraph", content: "Follow these steps to get set up." },
        { type: "checkListItem", content: "Create an account" },
      ],
    },
  ],
});

If you create a panel without specifying its children, it starts with an empty paragraph. To let users insert panels themselves, add a custom Slash Menu item, as shown in the demo.

To add blocks to an existing layout block, see Inserting Blocks.

Layout Blocks and Block Frames

Every custom block can have a frame, drawn by renderFrame around the block's content and its child blocks. A layout block is a block that has only a frame. The table shows the differences:

Block with a frameLayout block
ExampleAlert with a title and a bodyPanel, tab set, column list
ConfigAny content, usually "inline" or "plain"content: "none" and layoutOnly: true
ContentIts own text, drawn by renderNone
Implementationrender, and renderFrame if necessaryrenderFrame only
FramerenderFrame can return null to show the block without a frameAlways shown
Child blocksAny blocksAny blocks, or only the layout block types in children.allow
Without child blocksThe frame holds only the block's contentBy default, it always holds at least one child block (see Restricting Children)
KeyboardChild blocks behave like nested blocks, unless you change this with experimental_keyboardChild blocks cannot be moved out with Shift-Tab, and Enter in an empty last child block moves it out

Use a layout block when the block only holds and arranges other blocks. Use a block with a frame when the block has its own text, for example a title.

Restricting Children

For structured layouts, you can limit a layout block to specific layout block types, with these options in the block config:

children.allow: Accepts "blocks" (the default) or an array of layout block type names. You cannot list regular block types such as "paragraph" individually.

children.min: The minimum number of children, for an allow list only. Defaults to 1.

children.dissolve: Whether a layout block with too few children dissolves into what they hold, for an allow list only. Defaults to true, like a column list with one column left. Set false to keep the layout block and its empty children, like a tab set.

children.exclude: For a layout block that accepts any blocks, the layout block types it does not hold. Only layout block types can be excluded, not regular block types such as "paragraph".

placeable: Set to "namedOnly" to restrict a layout block to parents that name it in children.allow. Defaults to "anywhere".

The Tabs Block and Stepper Block examples use these options. A tab set holds only tab blocks, and a stepper holds only step blocks. Both set dissolve: false, so they stay when the user removes their last tab or step. A tab and a step are placeable: "namedOnly", so they can only be in a tab set or a stepper.

These options need layoutOnly: true. Other blocks can always have child blocks of any type, so for them children can only be the default, { allow: "blocks" }.

For built-in column blocks, see Multi-Column Layouts.