Suggestion Menus
Suggestion Menus appear when the user enters a trigger character, and text after the character is used to filter the menu items.
Slash Menu
The Slash Menu is a Suggestion Menu that opens with the /
character (or when clicking the +
button in the Block Side Menu.
Changing Slash Menu Items
You can change the items in the Slash Menu. The demo below adds an item that inserts a new block, with "Hello World" in bold.
import {
BlockNoteEditor,
filterSuggestionItems,
PartialBlock,
} from "@blocknote/core";
import "@blocknote/core/fonts/inter.css";
import {
DefaultReactSuggestionItem,
getDefaultReactSlashMenuItems,
SuggestionMenuController,
useCreateBlockNote,
} from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { HiOutlineGlobeAlt } from "react-icons/hi";
// Custom Slash Menu item to insert a block after the current one.
const insertHelloWorldItem = (editor: BlockNoteEditor) => ({
title: "Insert Hello World",
onItemClick: () => {
// Block that the text cursor is currently in.
const currentBlock = editor.getTextCursorPosition().block;
// New block we want to insert.
const helloWorldBlock: PartialBlock = {
type: "paragraph",
content: [{ type: "text", text: "Hello World", styles: { bold: true } }],
};
// Inserting the new block after the current one.
editor.insertBlocks([helloWorldBlock], currentBlock, "after");
},
aliases: ["helloworld", "hw"],
group: "Other",
icon: <HiOutlineGlobeAlt size={18} />,
subtext: "Used to insert a block with 'Hello World' below.",
});
// List containing all default Slash Menu Items, as well as our custom one.
const getCustomSlashMenuItems = (
editor: BlockNoteEditor
): DefaultReactSuggestionItem[] => [
...getDefaultReactSlashMenuItems(editor),
insertHelloWorldItem(editor),
];
export default function App() {
// Creates a new editor instance.
const editor = useCreateBlockNote({
initialContent: [
{
type: "paragraph",
content: "Welcome to this demo!",
},
{
type: "paragraph",
content: "Press the '/' key to open the Slash Menu",
},
{
type: "paragraph",
content: "Notice the new 'Insert Hello World' item - try it out!",
},
{
type: "paragraph",
},
],
});
// Renders the editor instance.
return (
<BlockNoteView editor={editor} slashMenu={false}>
<SuggestionMenuController
triggerCharacter={"/"}
// Replaces the default Slash Menu items with our custom ones.
getItems={async (query) =>
filterSuggestionItems(getCustomSlashMenuItems(editor), query)
}
/>
</BlockNoteView>
);
}
Passing slashMenu={false}
to BlockNoteView
tells BlockNote not to show the default Slash Menu. Adding the SuggestionMenuController
with triggerCharacter={"/"}
and a custom getItems
function tells BlockNote to show one with custom items instead.
getItems
should return the items that need to be shown in the Slash Menu, based on a query
entered by the user (anything the user types after the triggerCharacter
).
Replacing the Slash Menu Component
You can replace the React component used for the Slash Menu with your own, as you can see in the demo below.
import "@blocknote/core/fonts/inter.css";
import {
DefaultReactSuggestionItem,
SuggestionMenuController,
SuggestionMenuProps,
useCreateBlockNote,
} from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import "./styles.css";
// Custom component to replace the default Slash Menu.
function CustomSlashMenu(
props: SuggestionMenuProps<DefaultReactSuggestionItem>
) {
return (
<div className={"slash-menu"}>
{props.items.map((item, index) => (
<div
className={`slash-menu-item${
props.selectedIndex === index ? " selected" : ""
}`}
onClick={() => {
props.onItemClick?.(item);
}}>
{item.title}
</div>
))}
</div>
);
}
export default function App() {
// Creates a new editor instance.
const editor = useCreateBlockNote({
initialContent: [
{
type: "paragraph",
content: "Welcome to this demo!",
},
{
type: "paragraph",
content: "Press the '/' key to open the Slash Menu",
},
{
type: "paragraph",
content: "It's been replaced with a custom component",
},
{
type: "paragraph",
},
],
});
// Renders the editor instance.
return (
<BlockNoteView editor={editor} slashMenu={false}>
<SuggestionMenuController
triggerCharacter={"/"}
suggestionMenuComponent={CustomSlashMenu}
/>
</BlockNoteView>
);
}
Again, we add a SuggestionMenuController
component with triggerCharacter={"/"}
and set slashMenu={false}
to replace the default Slash Menu.
Now, we also pass a component to its suggestionMenuComponent
prop. The suggestionMenuComponent
we pass is responsible for rendering the filtered items. The SuggestionMenuController
controls its position and visibility (below the trigger character), and it also determines which items should be shown (using the optional getItems
prop we've seen above).
Creating additional Suggestion Menus
You can add additional Suggestion Menus to the editor, which can use any trigger character. The demo below adds an example Suggestion Menu for mentions, which opens with the @
character.
import {
BlockNoteSchema,
defaultInlineContentSpecs,
filterSuggestionItems,
} from "@blocknote/core";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import {
DefaultReactSuggestionItem,
SuggestionMenuController,
useCreateBlockNote,
} from "@blocknote/react";
import { Mention } from "./Mention";
// Our schema with inline content specs, which contain the configs and
// implementations for inline content that we want our editor to use.
const schema = BlockNoteSchema.create({
inlineContentSpecs: {
// Adds all default inline content.
...defaultInlineContentSpecs,
// Adds the mention tag.
mention: Mention,
},
});
// Function which gets all users for the mentions menu.
const getMentionMenuItems = (
editor: typeof schema.BlockNoteEditor
): DefaultReactSuggestionItem[] => {
const users = ["Steve", "Bob", "Joe", "Mike"];
return users.map((user) => ({
title: user,
onItemClick: () => {
editor.insertInlineContent([
{
type: "mention",
props: {
user,
},
},
" ", // add a space after the mention
]);
},
}));
};
export function App() {
const editor = useCreateBlockNote({
schema,
initialContent: [
{
type: "paragraph",
content: "Welcome to this demo!",
},
{
type: "paragraph",
content: [
{
type: "mention",
props: {
user: "Steve",
},
},
{
type: "text",
text: " <- This is an example mention",
styles: {},
},
],
},
{
type: "paragraph",
content: "Press the '@' key to open the mentions menu and add another",
},
{
type: "paragraph",
},
],
});
return (
<BlockNoteView editor={editor}>
{/* Adds a mentions menu which opens with the "@" key */}
<SuggestionMenuController
triggerCharacter={"@"}
getItems={async (query) =>
// Gets the mentions menu items
filterSuggestionItems(getMentionMenuItems(editor), query)
}
/>
</BlockNoteView>
);
}
export default App;
Changing the items in the new Suggestion Menu, or the component used to render it, is done the same way as for the Slash Menu. For more information about how the mentions elements work, see Custom Inline Content.
Additional Features
BlockNote offers a few other features for working with Suggestion Menus which may fit your use case.
Opening Suggestion Menus Programmatically
While suggestion menus are generally meant to be opened when the user presses a trigger character, you may also want to open them from code. To do this, you can use the following editor method:
openSuggestionMenu(triggerCharacter: string): void;
// Usage
editor.openSuggestionMenu("/");
Waiting for a Query
You may want to hold off displaying a Suggestion Menu unless you're certain that the user actually wants to open the menu and not just enter the trigger character. In this case, you should use the minQueryLength
prop for SuggestionMenuController
, which takes a number.
The number indicates how many characters the user query needs to have before the menu is shown. When greater than 0, it also prevents the menu from displaying if the user enters a space immediately after the trigger character.