The Toolbar component provides a fixed or floating bar with buttons for common editor operations like formatting text, inserting content, and managing lists.
Features
- Pre-built buttons for common formatting operations
- Automatic state management (active/disabled states)
- Customizable button layout
- Tooltip support
- Framework-agnostic implementation
Installation
import { ProseKit } from 'prosekit/react'
import { useEditor } from 'prosekit/react'
Basic Usage
import { useEditor, useEditorDerivedValue } from 'prosekit/react'
function Toolbar() {
const editor = useEditor()
const canBold = useEditorDerivedValue(
() => editor.commands.toggleBold.canExec()
)
const isBold = useEditorDerivedValue(
() => editor.marks.bold.isActive()
)
return (
<div className="toolbar">
<button
disabled={!canBold}
onClick={() => editor.commands.toggleBold()}
data-active={isBold}
>
Bold
</button>
</div>
)
}
Full Example
Here’s a complete toolbar with multiple formatting options:
import type { Editor } from 'prosekit/core'
import { useEditor, useEditorDerivedValue } from 'prosekit/react'
function getToolbarState(editor: Editor) {
return {
bold: {
isActive: editor.marks.bold.isActive(),
canExec: editor.commands.toggleBold.canExec(),
},
italic: {
isActive: editor.marks.italic.isActive(),
canExec: editor.commands.toggleItalic.canExec(),
},
underline: {
isActive: editor.marks.underline.isActive(),
canExec: editor.commands.toggleUnderline.canExec(),
},
}
}
export default function Toolbar() {
const editor = useEditor()
const state = useEditorDerivedValue(getToolbarState)
return (
<div className="toolbar">
<button
disabled={!state.bold.canExec}
data-active={state.bold.isActive}
onClick={() => editor.commands.toggleBold()}
>
Bold
</button>
<button
disabled={!state.italic.canExec}
data-active={state.italic.isActive}
onClick={() => editor.commands.toggleItalic()}
>
Italic
</button>
<button
disabled={!state.underline.canExec}
data-active={state.underline.isActive}
onClick={() => editor.commands.toggleUnderline()}
>
Underline
</button>
</div>
)
}
Here are common commands you can add to your toolbar:
Text Formatting
toggleBold() - Toggle bold formatting
toggleItalic() - Toggle italic formatting
toggleUnderline() - Toggle underline
toggleStrike() - Toggle strikethrough
toggleCode() - Toggle inline code
Block Types
toggleHeading({ level: 1 }) - Toggle heading (levels 1-6)
setParagraph() - Convert to paragraph
setCodeBlock() - Insert code block
toggleBlockquote() - Toggle blockquote
Lists
toggleList({ kind: 'bullet' }) - Toggle bullet list
toggleList({ kind: 'ordered' }) - Toggle numbered list
toggleList({ kind: 'task' }) - Toggle task list
indentList() - Increase indentation
dedentList() - Decrease indentation
History
undo() - Undo last change
redo() - Redo last undone change
Styling
The toolbar component doesn’t include default styles, giving you full control over appearance:
.toolbar {
display: flex;
gap: 4px;
padding: 8px;
border-bottom: 1px solid #e5e7eb;
}
.toolbar button {
padding: 6px 12px;
border: 1px solid transparent;
border-radius: 4px;
background: transparent;
cursor: pointer;
}
.toolbar button:hover {
background: #f3f4f6;
}
.toolbar button[data-active="true"] {
background: #e5e7eb;
}
.toolbar button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
Positioning
<div className="editor-container">
<Toolbar />
<div ref={editor.mount} />
</div>
For a floating toolbar, position it absolutely:
.toolbar {
position: sticky;
top: 0;
z-index: 10;
background: white;
}
API Reference
Editor Commands
All toolbar buttons typically use editor commands. Access them via:
editor.commands.commandName()
Editor State
Check active states and command availability:
// Check if a mark is active
editor.marks.bold.isActive()
// Check if a node is active
editor.nodes.heading.isActive({ level: 1 })
// Check if a command can be executed
editor.commands.toggleBold.canExec()
Hooks
Returns the current editor instance from context.
useEditorDerivedValue
<T>(fn: (editor: Editor) => T) => T
Computes a derived value from the editor state that updates on every editor change.
Best Practices
-
Use derived state: Always use
useEditorDerivedValue to compute toolbar state to ensure buttons update when the selection or document changes
-
Group related commands: Organize buttons into logical groups (formatting, blocks, lists, etc.)
-
Visual feedback: Show active states for toggle commands and disable buttons when commands can’t execute
-
Keyboard shortcuts: Consider showing keyboard shortcuts in tooltips
-
Responsive design: For mobile, consider collapsing less-used commands into a dropdown menu
See Also