Skip to content

BlockNote Integration

BlockNote Databases can be embedded directly inside a BlockNote block-based editor. Each database block renders a fully interactive table view within the editor document.

Create a database block specification and register it with BlockNote:

import { BlockNoteSchema, defaultBlockSpecs } from "@blocknote/core";
import { BlockNoteView } from "@blocknote/react";
import { useCreateBlockNote } from "@blocknote/react";
import "@blocknote/core/fonts/inter.css";
import "@blocknote/react/style.css";
import "@blocknote/block-view/react/styles.css";
import "@blocknote/block-view/react/adapters/styles.css";
import { createDatabaseBlockSpec, insertDatabaseBlockItem } from "@blocknote/block-view/blocknote";
import {
InMemoryCollectionSource,
InMemoryDocumentSource,
} from "@blocknote/block-view/core/sources/in-memory";
// Create the block specification
const databaseBlock = createDatabaseBlockSpec({
user: "user-1",
initialize: async ({ collectionId }) => {
// Return data sources for this collection
const collectionSource = new InMemoryCollectionSource(getCollectionData(collectionId));
const documentSource = new InMemoryDocumentSource(getDocumentsData(collectionId));
return { collectionSource, documentSource };
},
});
// Extend the BlockNote schema
const schema = BlockNoteSchema.create({
blockSpecs: {
...defaultBlockSpecs,
database: databaseBlock,
},
});
function MyEditor() {
const editor = useCreateBlockNote({
schema,
});
return (
<BlockNoteView
editor={editor}
slashMenu // enables the slash menu
/>
);
}

Add a /database command to the slash menu:

import { insertDatabaseBlockItem } from "@blocknote/block-view/blocknote";
const editor = useCreateBlockNote({
schema,
// The insertDatabaseBlockItem adds a "Database" option to the slash menu
});
// In your BlockNoteView, the slash menu automatically includes the database item
// when you use the schema that has the database block spec

insertDatabaseBlockItem creates a slash menu entry that inserts a new database block. When selected, it creates a block with empty collectionId and collectionViewId props.

Each database block stores three props:

PropTypeDescription
collectionIdstringID of the collection to display
collectionViewIdstringID of the specific view to use
viewTypestringView type (default: "table")

These are stored in the BlockNote document. The actual collection data lives in your data sources.

When a database block is inserted without a collectionId, it needs a way for the user to select or create a collection. Provide a collectionPicker component:

const databaseBlock = createDatabaseBlockSpec({
user: "user-1",
initialize: async ({ collectionId }) => ({
collectionSource: new InMemoryCollectionSource(getCollectionData(collectionId)),
documentSource: new InMemoryDocumentSource(getDocumentsData(collectionId)),
}),
collectionPicker: ({ onSelect, onCancel }) => (
<div style={{ padding: 16, border: "1px solid #ddd", borderRadius: 8 }}>
<p>Choose a collection:</p>
<button onClick={() => onSelect("tasks")}>Tasks</button>
<button onClick={() => onSelect("projects")}>Projects</button>
<button onClick={onCancel}>Cancel</button>
</div>
),
});

The picker receives onSelect(collectionId) to confirm a selection and onCancel to dismiss.

By default, the BlockNote editor and embedded database tables have independent undo/redo stacks. Use GlobalUndoRedoManager to unify them — so Ctrl+Z undoes the most recent action regardless of whether it was a text edit or a database change.

import { GlobalUndoRedoProvider, GlobalUndoRedoExtension } from "@blocknote/block-view/blocknote";
import { GlobalUndoRedoManager } from "@blocknote/block-view/core";
function MyEditor() {
// Create a shared undo/redo manager
const undoRedoManager = useMemo(() => new GlobalUndoRedoManager(), []);
const editor = useCreateBlockNote({
schema,
extensions: [
// Register BlockNote's undo/redo with the global manager
GlobalUndoRedoExtension.configure({ globalUndoRedoManager: undoRedoManager }),
],
});
return (
<GlobalUndoRedoProvider value={undoRedoManager}>
<BlockNoteView editor={editor} />
</GlobalUndoRedoProvider>
);
}

The GlobalUndoRedoProvider context makes the shared manager available to all database blocks rendered inside the editor. Each database block automatically registers its own UndoRedoManager as a source.

A single editor can contain multiple database blocks, each showing a different collection or a different view of the same collection:

// Initial editor content with multiple database blocks
const initialContent = [
{
type: "heading",
content: "Project Overview",
},
{
type: "database",
props: {
collectionId: "tasks",
collectionViewId: "view-1",
viewType: "table",
},
},
{
type: "paragraph",
content: "Here's the team roster:",
},
{
type: "database",
props: {
collectionId: "team-members",
collectionViewId: "view-2",
viewType: "table",
},
},
];
const editor = useCreateBlockNote({
schema,
initialContent,
});

Each block independently initializes its CollectionManager using the initialize function you provided to createDatabaseBlockSpec. They share the same GlobalUndoRedoManager if you set one up.

import { useMemo } from "react";
import { BlockNoteSchema, defaultBlockSpecs } from "@blocknote/core";
import { BlockNoteView, useCreateBlockNote } from "@blocknote/react";
import "@blocknote/core/fonts/inter.css";
import "@blocknote/react/style.css";
import "@blocknote/block-view/react/styles.css";
import "@blocknote/block-view/react/adapters/styles.css";
import {
createDatabaseBlockSpec,
insertDatabaseBlockItem,
GlobalUndoRedoProvider,
GlobalUndoRedoExtension,
} from "@blocknote/block-view/blocknote";
import { GlobalUndoRedoManager } from "@blocknote/block-view/core";
import {
InMemoryCollectionSource,
InMemoryDocumentSource,
} from "@blocknote/block-view/core/sources/in-memory";
// Your data loading functions
import { getCollectionData, getDocumentsData } from "./my-data";
const databaseBlock = createDatabaseBlockSpec({
user: "user-1",
initialize: async ({ collectionId }) => ({
collectionSource: new InMemoryCollectionSource(getCollectionData(collectionId)),
documentSource: new InMemoryDocumentSource(getDocumentsData(collectionId)),
}),
});
const schema = BlockNoteSchema.create({
blockSpecs: {
...defaultBlockSpecs,
database: databaseBlock,
},
});
export default function App() {
const undoRedoManager = useMemo(() => new GlobalUndoRedoManager(), []);
const editor = useCreateBlockNote({
schema,
extensions: [GlobalUndoRedoExtension.configure({ globalUndoRedoManager: undoRedoManager })],
});
return (
<GlobalUndoRedoProvider value={undoRedoManager}>
<BlockNoteView editor={editor} />
</GlobalUndoRedoProvider>
);
}