Events
BlockNote provides several event callbacks that allow you to respond to changes in the editor. These events are essential for building reactive applications and tracking user interactions.
Overview
The editor emits events for:
- Editor lifecycle - When the editor is created, mounted, unmounted, etc.
- Content changes - When blocks are inserted, updated, or deleted
- Selection changes - When the cursor position or selection changes
- Focus changes - When focus enters or leaves the editor
onMount
The onMount callback is called when the editor has been mounted.
editor.onMount(() => {
console.log("Editor is mounted");
});onUnmount
The onUnmount callback is called when the editor has been unmounted.
editor.onUnmount(() => {
console.log("Editor is unmounted");
});onSelectionChange
The onSelectionChange callback is called whenever the editor's selection changes, including cursor movements and text selections.
editor.onSelectionChange((editor) => {
console.log("Selection changed");
// Get current selection information
const selection = editor.getSelection();
const textCursorPosition = editor.getTextCursorPosition();
console.log("Current selection:", selection);
console.log("Text cursor position:", textCursorPosition);
});onFocusChange
The onFocusChange callback receives the editor and a context containing focused (a boolean) and event (the triggering DOM FocusEvent). By default, it reports focus entering or leaving the content area.
Pass includeEditorUI: true to count focus within the editor's toolbars, menus, and popovers too. In this mode, the callback runs only when that combined state changes, allowing focus to settle so moving from the content area into a popover input does not report a blur.
const unsubscribe = editor.onFocusChange(
(editor, { focused }) => {
console.log("Interacting with editor:", focused);
},
{ includeEditorUI: true },
);
// When you no longer need the listener:
unsubscribe();For the current focus state, use editor.isFocused(). In React, use useEditorFocus when focus determines what to render.
onChange
The onChange callback is called whenever the editor's content changes. This is the primary way to track modifications to the document.
editor.onChange((editor, { getChanges }) => {
console.log("Editor content changed");
// Get detailed information about what changed
const changes = getChanges();
console.log("Changes:", changes);
// Save content, update UI, etc.
});See Understanding Changes for more information about the getChanges function.
onBeforeChange
The onBeforeChange callback is called before any change is applied to the editor, allowing you to cancel the change.
editor.onBeforeChange(({ getChanges, tr }) => {
if (
// Cancel inserting new blocks
getChanges().some((change) => change.type === "insert")
) {
// By returning `false`, the change will be canceled & not applied to the editor.
return false;
}
});See Understanding Changes for more information about the getChanges function.
Understanding Changes
The getChanges() function returns detailed information about what blocks were affected. It includes three types of changes:
- Insertions - When a new block is inserted
- Deletions - When a block is deleted
- Updates - When a block's content is changed
- Moves - When a block is moved to a new position (which can also update the block's content)
/**
* The changes that occurred in the editor.
*/
type BlocksChanged = Array<
| {
type: "insert" | "delete";
// The affected block (when inserting, this is the new block, when deleting, this is the block that was deleted)
block: Block;
// The source of the change
source: BlockChangeSource;
// Insert and delete changes don't have a previous block
prevBlock: undefined;
}
| {
type: "update";
// The affected block
block: Block;
// The source of the change
source: BlockChangeSource;
// The block before the update
prevBlock: Block;
}
| {
type: "move";
// The source of the change
source: BlockChangeSource;
// The affected block
block: Block;
// The block before the move (since a move can also update the block's content)
prevBlock: Block;
/**
* The previous parent block (if it existed).
*/
prevParent?: Block;
/**
* The current parent block (if it exists).
*/
currentParent?: Block;
}
>;Change Sources
Each change includes a source that indicates what triggered the modification:
type BlockChangeSource = {
type:
| "local" // Triggered by local user (default)
| "paste" // From paste operation
| "drop" // From drop operation
| "undo" // From undo operation (local-only)
| "redo" // From redo operation (local-only)
| "undo-redo" // From undo/redo operations (collaboration-only)
| "yjs-remote"; // From remote user (collaboration-only)
};Event Cleanup
All event callbacks return cleanup functions that you can call to remove the event listener:
// Set up event listeners
const cleanupOnChange = editor.onChange((editor, { getChanges }) => {
console.log("Content changed");
});
const cleanupOnSelection = editor.onSelectionChange((editor) => {
console.log("Selection changed");
});
// Later, clean up event listeners
cleanupOnChange();
cleanupOnSelection();