ExamplesCustom SchemasLayout Block

Layout Block

In this example, we create a custom Panel block that holds other blocks as its body, such as a panel containing headings and paragraphs.

The block sets layoutOnly: true on BlockConfig, which makes it a layout block: it has no content of its own, only child blocks. It draws its box in renderFrame, and its child blocks go where slotRef is attached. At runtime, they are in block.children.

We also wire up a Slash Menu item to insert the panel.

Try it out:

  • Press the "/" key inside the panel's body and add a code block, heading, or list.
  • Insert a new panel via the Slash Menu (search "panel").

Relevant Docs:

import { BlockNoteSchema } from "@blocknote/core";import {  filterSuggestionItems,  insertOrUpdateBlockForSlashMenu,} from "@blocknote/core/extensions";import "@blocknote/core/fonts/inter.css";import { BlockNoteView } from "@blocknote/mantine";import "@blocknote/mantine/style.css";import {  SuggestionMenuController,  getDefaultReactSlashMenuItems,  useCreateBlockNote,} from "@blocknote/react";import { RiChatQuoteLine } from "react-icons/ri";import { createPanel } from "./Panel";import "./styles.css";// Schema with the default blocks plus our custom Panel layout block.const schema = BlockNoteSchema.create().extend({  blockSpecs: {    panel: createPanel(),  },});// Slash menu item to insert a Panel. Inserting one with no children fills// it with an empty paragraph: a layout block always holds at least one child.function insertPanel(editor: typeof schema.BlockNoteEditor) {  return {    title: "Panel",    subtext: "Layout block that wraps other blocks",    onItemClick: () =>      insertOrUpdateBlockForSlashMenu(editor, {        type: "panel",      }),    aliases: ["panel", "layout", "container", "box", "group", "section"],    group: "Basic blocks",    icon: <RiChatQuoteLine />,  };}export default function App() {  const editor = useCreateBlockNote({    schema,    initialContent: [      {        type: "paragraph",        content: "Welcome! This demo shows the new layout block kind.",      },      {        type: "panel",        children: [          {            type: "heading",            props: { level: 3 },            content: "More than paragraphs",          },          {            type: "paragraph",            content: "Panels can hold any block as their body.",          },          {            type: "paragraph",            content:              "Try pressing '/' inside this panel to add a heading or code block.",          },        ],      },      {        type: "paragraph",        content: "Press '/' anywhere to insert a new Panel.",      },      {        type: "paragraph",      },    ],  });  return (    <BlockNoteView editor={editor} slashMenu={false}>      <SuggestionMenuController        triggerCharacter={"/"}        getItems={async (query) =>          filterSuggestionItems(            [...getDefaultReactSlashMenuItems(editor), insertPanel(editor)],            query,          )        }      />    </BlockNoteView>  );}