Extensions
BlockNote includes an extensions system which lets you expand the editor's behaviour. Extensions can include any of the following features:
- Keyboard shortcuts
- Input rules
- ProseMirror plugins
- TipTap extensions
Creating an extension
You can create extensions using the createExtension function:
type Extension = {
key: string;
keyboardShortcuts?: Record<
string,
(ctx: { editor: BlockNoteEditor; }) => boolean
>;
inputRules?: {
find: RegExp;
replace: (props: {
match: RegExpMatchArray;
range: { from: number; to: number };
editor: BlockNoteEditor;
}) =>
| PartialBlock
| { text: string; styles: Record<string, string | boolean> }
| PartialInlineContent
| undefined;
}[];
prosemirrorPlugins?: Plugin[];
tiptapExtensions?: AnyExtension[];
}
const CustomExtension = createExtension({
key: "customBlockExtension",
keyboardShortcuts: ...,
inputRules: ...,
prosemirrorPlugins: ...,
tiptapExtensions: ...,
});Let's go over the options that can be passed into createExtension:
key: The name of the extension.
keyboardShortcuts?: Keyboard shortcuts can be used to run code when a key combination is pressed in the editor. The key names are the same as those used in the KeyboardEvent.key property. Takes an object which maps key name combinations (e.g. Meta+Shift+ArrowDown) to functions, which return true when the key press event is handled, or false otherwise. The functions have a single argument:
ctx:An object containing the BlockNote editor instance.
inputRules?: An array of declarative input rules for blocks, styles, or inline content. See Input rules.
prosemirrorPlugins?: An array of ProseMirror plugins.
tiptapExtensions?: An array of TipTap extensions.
Input rules
Input rules are plain objects with find and replace. For example, convert a paragraph starting with # into a heading:
import { createExtension } from "@blocknote/core";
const HeadingShortcut = createExtension({
key: "heading-shortcut",
inputRules: [
{
find: /^#\s$/,
replace: () => ({ type: "heading", props: { level: 1 } }),
},
],
});The replace callback receives { editor, match, range }. Return a PartialBlock to convert the current block, or undefined to leave it unchanged. Undo restores the complete literal text, including the trigger.
The returned shape determines the action: { text, styles } applies styles, a string or array replaces inline content, and a partial block updates the current block:
import { createExtension } from "@blocknote/core";
const TextShortcuts = createExtension({
key: "text-shortcuts",
inputRules: [
{
find: /\*\*([^*]+)\*\*$/,
replace: ({ match }) => ({ text: match[1], styles: { bold: true } }),
},
{
find: /->$/,
replace: () => "→",
},
],
});Style rules retain the returned text from the match, remove its surrounding delimiters (preserving leading whitespace), and apply styles without discarding existing formatting. Inline rules replace the whole match with a string or an array of BlockNote inline content, including custom inline content. Either kind can return undefined to decline the match. Style-associated extensions can be colocated with their style spec.
Block rules also match plain Enter as a newline. Style and inline rules run only on text input. Register rules when creating the editor; adding input rules at runtime is not supported.
Custom TipTap extensions can still use addInputRules, but those rules keep TipTap's own input-rule and undo behavior rather than the shared BlockNote engine.
Adding extensions to the editor
Extensions can be added to the editor on their own via the editor options or as part of custom blocks.
Adding directly to the editor
The extensions editor option takes an array of extensions to be added to the editor:
const editor = useCreateBlockNote({
extensions: [
// Add extensions here:
createExtension({ ... })
],
});Adding to custom blocks
When creating a custom block using createReactBlockSpec, you can pass an array of extensions to the third parameter:
const createCustomBlock = createReactBlockSpec(
{
// Block config
...
},
{
// Block implementation
...
}
[
// Add extensions here:
createExtension({ ... })
],
});