@slash-editor/react
Hooks and headless primitives over @tiptap/react.
Every hook here composes a core extension's editor.storage (subscribe/state) with the
extension's own commands — none owns behavior the core doesn't already expose.
useSlashEditor
useSlashEditor(options?: UseSlashEditorOptions): Editor | nullExtends @tiptap/react's useEditor options with two fields:
| Option | Type | Description |
|---|---|---|
blockKit? | BlockKitOptions | false | Baseline block schema options, or false to start from an empty schema and supply everything through extensions. See the block kit guide. |
extensions? | Extensions | Extensions appended after the block kit. |
Every other option passes through to useEditor. Two defaults differ: immediatelyRender: false
(so the same component renders under SSR without a hydration mismatch), and extensions are
resolved once per editor instance — ProseMirror cannot swap a schema on a live document, so later
changes to blockKit/extensions are ignored rather than silently half-applied.
useSlashMenu
useSlashMenu(editor: Editor | null): SlashMenuExposes the slash command's ranked state plus a caret anchor:
interface SlashMenu {
open: boolean;
query: string;
items: SlashItem[];
activeIndex: number;
activeItem: SlashItem | null;
anchor: SlashMenuAnchor | null;
setActiveIndex: (index: number) => void;
select: (index?: number) => void;
close: () => void;
}useBubbleToolbar
useBubbleToolbar(editor: Editor | null): BubbleToolbarSelection-anchored formatting state — items already filtered by each item's when.
useMention
useMention(editor: Editor | null): MentionMenuSame subscribe/setActiveIndex/select/close shape as useSlashMenu, plus loading while
the async provider's promise for the current query is in flight. See the
mentions guide.
useEmoji
useEmoji(editor: Editor | null): EmojiMenuSame subscribe/setActiveIndex/select/close shape as useSlashMenu; items are
EmojiItems ranked for the query. See the emoji guide.
useLinkEditor
useLinkEditor(editor: Editor | null): LinkEditorOwns only visibility and a draft href — applying or removing the link calls the link mark's
own setLink/unsetLink directly.
useComments
useComments(editor: Editor | null): CommentsReads the CommentThreadStore configured through createBlockKit({ comment: { store } }),
subscribes to store changes, and exposes the selection-driven composer state. See the
comments guide.
useResolvedSrc
useResolvedSrc(src: string | null | undefined, node: Node, resolveSrc: ResolveSrc | undefined): string | nullThe display URL for a media node view: src passed through the node's display-only resolveSrc
option (props.extension.options.resolveSrc). Sync results return on the same render; async
results yield null until they settle, a result for a superseded src is discarded, and a thrown
error or rejection falls back to src. See the
upload adapter guide.
usePresence
usePresence(provider: PresenceProvider | null): PresencePeer[]Reads any awareness-shaped provider — the same duck-typed surface collaboration uses — into a plain array of connected peers.
Pages hooks
usePage(store: PageStore, pageId: string | null | undefined): { page: PageMeta | undefined; status: "loading" | "ready" | "missing" }
usePageTree(store: PageStore, rootId?: string | null, options?: { defaultExpanded?: string[] }): { rows; toggle; expand; collapse }
useBreadcrumb(store: PageStore, pageId: string | null | undefined): PageMeta[]
useBacklinks(store: PageStore, pageId: string | null | undefined): { pages: PageMeta[]; loading: boolean }Editor-independent, like usePresence: they read a PageStore directly. See the
pages guide and Pages components.
useActiveItemScroll
useActiveItemScroll(options: ActiveItemScrollOptions): voidProp
Type
Keeps a menu's active row scrolled into view as activeIndex changes — used by both
Slash Menu and Mention Menu.
useBlockDrag
useBlockDrag(editor: Editor | null): BlockDragDrag/insert-handle state consumed by Block Handle.
Re-exported from @tiptap/react
EditorContent, EditorContext, EditorProvider, useCurrentEditor, useEditorState —
unchanged, re-exported so a consumer never needs a direct @tiptap/react import for the common
path.