Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
b94e7ee
refactor(extensions): rewrite to use new extension system
nperez0111 Oct 31, 2025
916fc8c
chore: minor fixes
nperez0111 Nov 3, 2025
6a0150c
Refactored almost all formatting toolbar components to use `useEditor…
matthewlipski Oct 29, 2025
06fa6f7
Refactored remaining uses of hooks with `useEditorState` where possib…
matthewlipski Oct 30, 2025
5ffc5a4
feat: virtual element hooks
nperez0111 Nov 3, 2025
9b266e5
Merge branch 'plugin-system' of github.com:TypeCellOS/BlockNote into …
matthewlipski Nov 4, 2025
1073248
Refactored formatting toolbar, file panel, and suggestion menu contro…
matthewlipski Nov 5, 2025
343b67c
Fixed popover position reference not updating when it should & fixed …
matthewlipski Nov 9, 2025
f80999c
Cleaned up hooks
matthewlipski Nov 11, 2025
a1372c0
- Cleaned up refactored UI plugins
matthewlipski Nov 12, 2025
555973d
Refactored side menu & fixed issue with `GenericPopover` memoized `in…
matthewlipski Nov 12, 2025
d07e2a4
Comment edit
matthewlipski Nov 13, 2025
369606c
Fixed core build
matthewlipski Nov 13, 2025
a5287b4
Misc fixes
matthewlipski Nov 17, 2025
2b2d3a2
Small fix
matthewlipski Nov 17, 2025
050ecff
Refactored table handles
matthewlipski Nov 17, 2025
84d303c
Fixed various build & UI issues
matthewlipski Nov 18, 2025
988f3de
Fixed various UI issues
matthewlipski Nov 18, 2025
7693564
Fixed link creation/editing throwing error
matthewlipski Nov 19, 2025
ccfa56d
Refactored comments
matthewlipski Nov 19, 2025
e01c25c
chore: update prosemirror versions
nperez0111 Nov 19, 2025
5ce9ad7
fix: y-prosemirror plugin ordering
nperez0111 Nov 19, 2025
bd5a73d
Updated comments to fully use new components APIs
matthewlipski Nov 19, 2025
7972e49
chore: fix build
nperez0111 Nov 19, 2025
3b568d2
build: get multi-column building again
nperez0111 Nov 19, 2025
e64aef7
refactor: rewrite the AI extension to use the new plugin API
nperez0111 Nov 20, 2025
0c3c37a
fix: make mounting and unmounting extensions actually work properly
nperez0111 Nov 20, 2025
3c8992a
Fixed z-index clipping issues
matthewlipski Nov 20, 2025
911d01a
Merge branch 'plugin-system' of github.com:TypeCellOS/BlockNote into …
matthewlipski Nov 20, 2025
e92b294
refactor: normalize extension naming no longer plugins
nperez0111 Nov 20, 2025
81c0032
refactor: always turn a DOMRect into JSON for easy comparisons
nperez0111 Nov 20, 2025
16e87ab
refactor: change imports of extensions to be found at `@blocknote/cor…
nperez0111 Nov 20, 2025
d9c8b65
fix: use an abort signal instead of sending an abortcontroller
nperez0111 Nov 20, 2025
beb11fc
chore: fix circular import
nperez0111 Nov 20, 2025
8fb1459
chore: use any blocknote editor
nperez0111 Nov 20, 2025
4be8534
refactor: less derived state
nperez0111 Nov 20, 2025
d40518b
refactor: rename `usePlugin` -> `useExtension`
nperez0111 Nov 20, 2025
2fb11fa
refactor: remove class member
nperez0111 Nov 20, 2025
0190ba2
refactor: rename `plugins` -> `prosemirrorPlugins` and `init` -> `mount`
nperez0111 Nov 20, 2025
5157ce8
Fixed suggestion menus closing when clicking scroll bar
matthewlipski Nov 20, 2025
171ef89
Merge branch 'plugin-system' of github.com:TypeCellOS/BlockNote into …
matthewlipski Nov 20, 2025
f57ef14
fix: always pull the latest extensions in React
nperez0111 Nov 20, 2025
dd098b0
Fixed table handle issues
matthewlipski Nov 21, 2025
f4bab3b
add comments
YousefED Nov 21, 2025
9b69258
Merge branch 'plugin-system' of github.com:TypeCellOS/BlockNote into …
YousefED Nov 21, 2025
6f901a2
Fixed most remaining table handle issues
matthewlipski Nov 21, 2025
c1b1b3d
Merge branch 'plugin-system' of github.com:TypeCellOS/BlockNote into …
matthewlipski Nov 21, 2025
981aedc
fix: formatting toolbar, comments + cleanup
nperez0111 Nov 21, 2025
0be31ed
refactor: filepanel unsubs it's listeners
nperez0111 Nov 21, 2025
4300600
Merge branch 'main' into plugin-system
matthewlipski Nov 21, 2025
bba68f2
Fixed build
matthewlipski Nov 21, 2025
73b4c75
refactor: rewrite how extensions work
nperez0111 Nov 24, 2025
da8e039
chore: fix lint
nperez0111 Nov 24, 2025
8eed1c8
Fixed formatting & link toolbar issues
matthewlipski Nov 24, 2025
b4138c4
Merge branch 'plugin-system' of github.com:TypeCellOS/BlockNote into …
matthewlipski Nov 24, 2025
d6027ca
Small fix
matthewlipski Nov 24, 2025
3bfbb00
refactor: PR comments
nperez0111 Nov 24, 2025
55ab3ce
Small fix
matthewlipski Nov 24, 2025
a6be872
Merge branch 'plugin-system' of github.com:TypeCellOS/BlockNote into …
matthewlipski Nov 24, 2025
eaa1fe2
fix: handle collaboration again
nperez0111 Nov 24, 2025
1c5af23
refactor: remove resolveUsers from editor
nperez0111 Nov 24, 2025
3524f24
refactor: cleanup some more off the editor
nperez0111 Nov 24, 2025
da6b0a5
remove zustand dep
YousefED Nov 24, 2025
860c5f4
Merge branch 'plugin-system' of github.com:TypeCellOS/BlockNote into …
YousefED Nov 24, 2025
383b3df
refactor: move comments to their sub-package
nperez0111 Nov 24, 2025
89a6d96
refactor: rename extensions to have `Extension` suffix
nperez0111 Nov 24, 2025
12c85ac
refactor: delete some un-used hooks
nperez0111 Nov 24, 2025
208e967
fix: remove comments options from the editor instance
nperez0111 Nov 24, 2025
14a9ee4
Fixed suggestion menu issues
matthewlipski Nov 24, 2025
c0ea16b
Merge branch 'plugin-system' of github.com:TypeCellOS/BlockNote into …
matthewlipski Nov 24, 2025
8f7b3e4
Misc changes/fixes
matthewlipski Nov 24, 2025
ce5afa8
Small fix
matthewlipski Nov 24, 2025
7608953
fix: put back menu handling
nperez0111 Nov 24, 2025
38a5fc2
Removed logs
matthewlipski Nov 24, 2025
37a613b
Merge branch 'plugin-system' of github.com:TypeCellOS/BlockNote into …
matthewlipski Nov 24, 2025
3dbe824
refactor(comments): make extension use store exclusively
nperez0111 Nov 24, 2025
7346e55
fix: break circular import
nperez0111 Nov 24, 2025
869aa94
fix: remove `onCreate` event since it is not needed with current system
nperez0111 Nov 24, 2025
c4a4b0c
Formatting toolbar and file panel fixes
matthewlipski Nov 24, 2025
6260e9d
Merge branch 'plugin-system' of github.com:TypeCellOS/BlockNote into …
matthewlipski Nov 24, 2025
fa1adee
test: add some checks for tests
nperez0111 Nov 24, 2025
8b46780
fix: use correct key
nperez0111 Nov 24, 2025
dc8373b
Fixed tests
matthewlipski Nov 24, 2025
d9c85f1
Fixed collab tests
matthewlipski Nov 24, 2025
57a01f2
test: fixes for test running
nperez0111 Nov 25, 2025
91207f4
test: get all unit tests passing again
nperez0111 Nov 25, 2025
04acea9
chore: fix lint
nperez0111 Nov 25, 2025
294f0fe
build: lint depends on nothing
nperez0111 Nov 25, 2025
02d54e1
Refactored/tested/fixed AI menu and other misc changes
matthewlipski Nov 25, 2025
17a5905
Merge branch 'plugin-system' of github.com:TypeCellOS/BlockNote into …
matthewlipski Nov 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/content/docs/features/ai/custom-commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ First, we define a new AI command. Let's create one that makes selected text mor

```tsx
import {
AIExtension,
AIMenuSuggestionItem,
getAIExtension,
aiDocumentFormats,
} from "@blocknote/xl-ai";

Expand All @@ -40,7 +40,7 @@ export const makeInformal = (
aliases: ["informal", "make informal", "casual"],
icon: <RiEmotionHappyFill size={18} />,
onItemClick: async () => {
await getAIExtension(editor).invokeAI({
await editor.getExtension(AIExtension)?.invokeAI({
// The prompt to send to the LLM:
userPrompt: "Give the selected text a more informal (casual) tone",
// Tell the LLM to specifically use the selected content as context (instead of the whole document)
Expand Down
8 changes: 0 additions & 8 deletions docs/content/docs/features/ai/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,6 @@ type AIRequestHelpers = {
};
```

## `getAIExtension`

Use `getAIExtension` to retrieve the AI extension instance registered to the editor:

```typescript
function getAIExtension(editor: BlockNoteEditor): AIExtension;
```

## `AIExtension`

The `AIExtension` class is the main class for the AI extension. It exposes state and methods to interact with BlockNote's AI features.
Expand Down
10 changes: 5 additions & 5 deletions docs/content/docs/features/extensions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ BlockNote includes an extensions system which lets you expand the editor's behav

## Creating an extension

An extension is an instance of the [`BlockNoteExtension`](https://github.com/TypeCellOS/BlockNote/blob/10cdbfb5f77ef82f3617c0fa1191e0bf5b7358c5/packages/core/src/editor/BlockNoteExtension.ts#L13) class. However, it's recommended for most use cases to create extensions using the `createBlockNoteExtension` function, rather than instanciating the class directly:
An extension is an instance of the [`BlockNoteExtension`](https://github.com/TypeCellOS/BlockNote/blob/10cdbfb5f77ef82f3617c0fa1191e0bf5b7358c5/packages/core/src/editor/BlockNoteExtension.ts#L13) class. However, it's recommended for most use cases to create extensions using the `createExtension` function, rather than instanciating the class directly:

```typescript
type BlockNoteExtensionOptions = {
Expand Down Expand Up @@ -43,10 +43,10 @@ const customBlockExtensionOptions: BlockNoteExtensionOptions = {
tiptapExtensions: ...,
}

const CustomExtension = createBlockNoteExtension(customBlockExtensionOptions);
const CustomExtension = createExtension(customBlockExtensionOptions);
```

Let's go over the options that can be passed into `createBlockNoteExtension`:
Let's go over the options that can be passed into `createExtension`:

`key:` The name of the extension.

Expand Down Expand Up @@ -74,7 +74,7 @@ The `extensions` [editor option](/docs/reference/editor/overview#options) takes
const editor = useCreateBlockNote({
extensions: [
// Add extensions here:
createBlockNoteExtension({ ... })
createExtension({ ... })
],
});
```
Expand All @@ -95,7 +95,7 @@ const createCustomBlock = createReactBlockSpec(
}
[
// Add extensions here:
createBlockNoteExtension({ ... })
createExtension({ ... })
],
});
```
11 changes: 0 additions & 11 deletions docs/content/docs/reference/editor/events.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,6 @@ The editor emits events for:
- **Content changes** - When blocks are inserted, updated, or deleted
- **Selection changes** - When the cursor position or selection changes

## `onCreate`

The `onCreate` callback is called when the editor has been initialized and is ready for use.

```typescript
editor.onCreate(() => {
console.log("Editor is ready for use");
// Initialize plugins, set up event listeners, etc.
});
```

## `onMount`

The `onMount` callback is called when the editor has been mounted.
Expand Down
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
"better-sqlite3": "^11.10.0",
"classnames": "2.3.2",
"clsx": "2.1.1",
"docx": "^9.0.2",
"docx": "^9.5.1",
"framer-motion": "^11.18.2",
"fumadocs-core": "15.5.4",
"fumadocs-docgen": "2.0.1",
Expand Down
2 changes: 1 addition & 1 deletion examples/01-basic/03-multi-column/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
BlockNoteSchema,
combineByGroup,
filterSuggestionItems,
} from "@blocknote/core";
import { filterSuggestionItems } from "@blocknote/core/extensions";
import * as locales from "@blocknote/core/locales";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import "@blocknote/mantine/style.css";
import {
useBlockNoteEditor,
useComponentsContext,
useEditorContentOrSelectionChange,
useEditorState,
useSelectedBlocks,
} from "@blocknote/react";
import { useState } from "react";

// Custom Formatting Toolbar Button to toggle blue text & background color.
export function BlueButton() {
Expand All @@ -14,18 +13,12 @@ export function BlueButton() {
const Components = useComponentsContext()!;

// Tracks whether the text & background are both blue.
const [isSelected, setIsSelected] = useState<boolean>(
editor.getActiveStyles().textColor === "blue" &&
editor.getActiveStyles().backgroundColor === "blue",
);

// Updates state on content or selection change.
useEditorContentOrSelectionChange(() => {
setIsSelected(
const isSelected = useEditorState({
editor,
selector: ({ editor }) =>
editor.getActiveStyles().textColor === "blue" &&
editor.getActiveStyles().backgroundColor === "blue",
);
}, editor);
editor.getActiveStyles().backgroundColor === "blue",
});

// Doesn't render unless a at least one block with inline content is
// selected. You can use a similar pattern of returning `null` to
Expand Down
2 changes: 1 addition & 1 deletion examples/03-ui-components/04-side-menu-buttons/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default function App() {
sideMenu={(props) => (
<SideMenu {...props}>
{/* Button which removes the hovered block. */}
<RemoveBlockButton {...props} />
<RemoveBlockButton />
<DragHandleButton {...props} />
</SideMenu>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import {} from "@blocknote/core";
import { SideMenuExtension } from "@blocknote/core/extensions";
import {
SideMenuProps,
useBlockNoteEditor,
useComponentsContext,
useExtension,
} from "@blocknote/react";
import { MdDelete } from "react-icons/md";

// Custom Side Menu button to remove the hovered block.
export function RemoveBlockButton(props: SideMenuProps) {
export function RemoveBlockButton() {
const editor = useBlockNoteEditor();

const Components = useComponentsContext()!;

const sideMenu = useExtension(SideMenuExtension);

return (
<Components.SideMenu.Button
label="Remove block"
icon={
<MdDelete
size={24}
onClick={() => {
editor.removeBlocks([props.block]);
editor.removeBlocks([sideMenu.store.state!.block]);
}}
/>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import "@blocknote/mantine/style.css";
import {
BlockColorsItem,
DragHandleMenu,
DragHandleMenuProps,
RemoveBlockItem,
SideMenu,
SideMenuController,
Expand All @@ -16,12 +15,12 @@ import { ResetBlockTypeItem } from "./ResetBlockTypeItem";
// To avoid rendering issues, it's good practice to define your custom drag
// handle menu in a separate component, instead of inline within the `sideMenu`
// prop of `SideMenuController`.
const CustomDragHandleMenu = (props: DragHandleMenuProps) => (
<DragHandleMenu {...props}>
<RemoveBlockItem {...props}>Delete</RemoveBlockItem>
<BlockColorsItem {...props}>Colors</BlockColorsItem>
const CustomDragHandleMenu = () => (
<DragHandleMenu>
<RemoveBlockItem>Delete</RemoveBlockItem>
<BlockColorsItem>Colors</BlockColorsItem>
{/* Item which resets the hovered block's type. */}
<ResetBlockTypeItem {...props}>Reset Type</ResetBlockTypeItem>
<ResetBlockTypeItem>Reset Type</ResetBlockTypeItem>
</DragHandleMenu>
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import {} from "@blocknote/core";
import { SideMenuExtension } from "@blocknote/core/extensions";
import {
DragHandleMenuProps,
useBlockNoteEditor,
useComponentsContext,
useExtension,
} from "@blocknote/react";
import { ReactNode } from "react";

export function ResetBlockTypeItem(props: DragHandleMenuProps) {
export function ResetBlockTypeItem(props: { children: ReactNode }) {
const editor = useBlockNoteEditor();

const Components = useComponentsContext()!;

const sideMenu = useExtension(SideMenuExtension);

return (
<Components.Generic.Menu.Item
onClick={() => {
editor.updateBlock(props.block, { type: "paragraph" });
editor.updateBlock(sideMenu.store.state!.block, { type: "paragraph" });
}}
>
Reset Type
{props.children}
</Components.Generic.Menu.Item>
);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { BlockNoteEditor } from "@blocknote/core";
import {
BlockNoteEditor,
filterSuggestionItems,
insertOrUpdateBlock,
} from "@blocknote/core";
insertOrUpdateBlockForSlashMenu,
} from "@blocknote/core/extensions";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
Expand All @@ -22,7 +22,7 @@ const insertHelloWorldItem = (editor: BlockNoteEditor) => ({
// changes its type to the provided block. Otherwise, it inserts the new
// block below and moves the text caret to it. We use this function with
// a block containing 'Hello World' in bold.
insertOrUpdateBlock(editor, {
insertOrUpdateBlockForSlashMenu(editor, {
type: "paragraph",
content: [{ type: "text", text: "Hello World", styles: { bold: true } }],
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
BlockNoteSchema,
defaultInlineContentSpecs,
filterSuggestionItems,
} from "@blocknote/core";
import { filterSuggestionItems } from "@blocknote/core/extensions";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const FileReplaceButton = () => {
}

return (
<Components.Generic.Popover.Root opened={isOpen} position={"bottom"}>
<Components.Generic.Popover.Root open={isOpen} position={"bottom"}>
<Components.Generic.Popover.Trigger>
<Components.FormattingToolbar.Button
className={"bn-button"}
Expand All @@ -70,7 +70,7 @@ export const FileReplaceButton = () => {
variant={"panel-popover"}
>
{/* Replaces default file panel with our Uppy one. */}
<UppyFilePanel block={block as any} />
<UppyFilePanel blockId={block.id} />
</Components.Generic.Popover.Content>
</Components.Generic.Popover.Root>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const uppy = new Uppy()
});

export function UppyFilePanel(props: FilePanelProps) {
const { block } = props;
const { blockId } = props;
const editor = useBlockNoteEditor();

useEffect(() => {
Expand All @@ -68,7 +68,7 @@ export function UppyFilePanel(props: FilePanelProps) {
url: response.uploadURL,
},
};
editor.updateBlock(block, updateData);
editor.updateBlock(blockId, updateData);

// File should be removed from the Uppy instance after upload.
uppy.removeFile(file.id);
Expand All @@ -78,7 +78,7 @@ export function UppyFilePanel(props: FilePanelProps) {
return () => {
uppy.off("upload-success", handler);
};
}, [block, editor]);
}, [blockId, editor]);

// set up dashboard as in https://uppy.io/examples/
return <Dashboard uppy={uppy} width={400} height={500} />;
Expand Down
3 changes: 2 additions & 1 deletion examples/03-ui-components/13-custom-ui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { filterSuggestionItems } from "@blocknote/core";
import { } from "@blocknote/core";
import { filterSuggestionItems } from "@blocknote/core/extensions";
import "@blocknote/core/fonts/inter.css";
import {
BlockNoteViewRaw,
Expand Down
Loading
Loading