Container 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 declares the children config on BlockConfig. children: { allow: "blocks" } makes it a container: its child blocks mount into the rendered content region (attached with ref={contentRef}), and live on block.children at runtime. A pure container like this draws its box in render, which re-renders live when props change.
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 container 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, as `min` defaults to 1.function insertPanel(editor: typeof schema.BlockNoteEditor) { return { title: "Panel", subtext: "Container block that wraps other blocks", onItemClick: () => insertOrUpdateBlockForSlashMenu(editor, { type: "panel", }), aliases: ["panel", "container", "callout", "alert", "note", "tip", "info"], group: "Basic blocks", icon: <RiChatQuoteLine />, };}export default function App() { const editor = useCreateBlockNote({ schema, initialContent: [ { type: "paragraph", content: "Welcome! This demo shows the new container 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> );}import { createReactBlockSpec } from "@blocknote/react";import "./styles.css";export const createPanel = createReactBlockSpec( { type: "panel", propSchema: {}, content: "none", children: { allow: "blocks" }, }, { // With no content of its own, contentRef receives the child blocks. render: (props) => <div className="panel" ref={props.contentRef} />, },);.panel { border-left: 4px solid #507aff; border-radius: 6px; background-color: var(--panel-bg, #e6ebff); padding: 12px 16px;}[data-color-scheme="dark"] .panel { --panel-bg: #1e2a5c;}