Skip to content
GitHub

Using Extensions

Extensions are at the heart of ProseKit’s flexibility and power. They allow you to add functionality to your editor in a modular way, making it easy to build exactly the editing experience you need.

Extensions in ProseKit are modular pieces of functionality that you can add to your editor. They can provide:

  • Nodes (paragraphs, headings, lists, code blocks, etc.)
  • Marks (bold, italic, underline, etc.)
  • Commands (functions that modify the document)
  • Keyboard shortcuts
  • Input rules (automatic transformations as you type)
  • Custom behaviors and much more

ProseKit provides a defineBasicExtension() function that includes the most common features:

import { defineBasicExtensionfunction defineBasicExtension(): BasicExtension
Define a basic extension that includes some common functionality. You can copy this function and customize it to your needs. It's a combination of the following extension functions: - {@link defineDoc } - {@link defineText } - {@link defineParagraph } - {@link defineHeading } - {@link defineList } - {@link defineBlockquote } - {@link defineImage } - {@link defineHorizontalRule } - {@link defineTable } - {@link defineCodeBlock } - {@link defineItalic } - {@link defineBold } - {@link defineUnderline } - {@link defineStrike } - {@link defineCode } - {@link defineLink } - {@link defineBaseKeymap } - {@link defineBaseCommands } - {@link defineHistory } - {@link defineDropCursor } - {@link defineGapCursor } - {@link defineVirtualSelection } - {@link defineModClickPrevention }
@public
} from 'prosekit/basic'
import { createEditorfunction createEditor<E extends Extension>(options: EditorOptions<E>): Editor<E>
@public
} from 'prosekit/core'
// Create an editor with the extension const editorconst editor: Editor<BasicExtension> = createEditorcreateEditor<BasicExtension>(options: EditorOptions<BasicExtension>): Editor<BasicExtension>
@public
({ extensionEditorOptions<BasicExtension>.extension: BasicExtension
The extension to use when creating the editor.
: defineBasicExtensionfunction defineBasicExtension(): BasicExtension
Define a basic extension that includes some common functionality. You can copy this function and customize it to your needs. It's a combination of the following extension functions: - {@link defineDoc } - {@link defineText } - {@link defineParagraph } - {@link defineHeading } - {@link defineList } - {@link defineBlockquote } - {@link defineImage } - {@link defineHorizontalRule } - {@link defineTable } - {@link defineCodeBlock } - {@link defineItalic } - {@link defineBold } - {@link defineUnderline } - {@link defineStrike } - {@link defineCode } - {@link defineLink } - {@link defineBaseKeymap } - {@link defineBaseCommands } - {@link defineHistory } - {@link defineDropCursor } - {@link defineGapCursor } - {@link defineVirtualSelection } - {@link defineModClickPrevention }
@public
() })

A key concept in ProseKit is that all extensions are composed of other extensions using the union function. This composability lets you create complex functionality by combining simpler building blocks.

For example, defineBasicExtension is simply a composition of many smaller extensions:

import {
  defineBaseCommandsfunction defineBaseCommands(): BaseCommandsExtension
Add some base commands
@public
,
defineBaseKeymap
function defineBaseKeymap(options?: {
    priority?: Priority;
}): BaseKeymapExtension
Defines some basic key bindings.
@public
,
defineHistoryfunction defineHistory({ depth, newGroupDelay, }?: HistoryOptions): HistoryExtension
Add undo/redo history to the editor.
@paramoptions@public
,
unionfunction union<const E extends readonly Extension[]>(...exts: E): Union<E> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```@public
,
} from 'prosekit/core' import { defineBlockquotefunction defineBlockquote(): BlockquoteExtension
@public
} from 'prosekit/extensions/blockquote'
import { defineBoldfunction defineBold(): BoldExtension
@public
} from 'prosekit/extensions/bold'
import { defineCodefunction defineCode(): CodeExtension
@public
} from 'prosekit/extensions/code'
import { defineCodeBlockfunction defineCodeBlock(): CodeBlockExtension
Adds `codeBlock` nodes to the editor. This includes the following extensions: - {@link defineCodeBlockSpec } - {@link defineCodeBlockInputRule } - {@link defineCodeBlockEnterRule } - {@link defineCodeBlockKeymap } - {@link defineCodeBlockCommands } .
@public
} from 'prosekit/extensions/code-block'
import { defineDocfunction defineDoc(): DocExtension
@public
} from 'prosekit/extensions/doc'
import { defineDropCursorfunction defineDropCursor(options?: DropCursorOptions): DropCursorExtension
Show up a decoration at the drop position when something is dragged over the editor. See [prosemirror-dropcursor](https://github.com/ProseMirror/prosemirror-dropcursor) for more information.
@public
} from 'prosekit/extensions/drop-cursor'
import { defineGapCursorfunction defineGapCursor(): GapCursorExtension
Capture clicks near and arrow-key-motion past places that don't have a normally selectable position nearby, and create a gap cursor selection for them. The cursor is drawn as an element with class `ProseMirror-gapcursor`. You can either include `prosekit/extensions/gap-cursor.css` or add your own styles to make it visible. See [prosemirror-gapcursor](https://github.com/ProseMirror/prosemirror-gapcursor) for more information.
@public
} from 'prosekit/extensions/gap-cursor'
import { defineHeadingfunction defineHeading(): HeadingExtension
@public
} from 'prosekit/extensions/heading'
import { defineHorizontalRulefunction defineHorizontalRule(): HorizontalRuleExtension
@public
} from 'prosekit/extensions/horizontal-rule'
import { defineImagefunction defineImage(): ImageExtension
@public
} from 'prosekit/extensions/image'
import { defineItalicfunction defineItalic(): ItalicExtension
@public
} from 'prosekit/extensions/italic'
import { defineLinkfunction defineLink(): LinkExtension
@public
} from 'prosekit/extensions/link'
import { defineListfunction defineList(): ListExtension
@public
} from 'prosekit/extensions/list'
import { defineModClickPreventionfunction defineModClickPrevention(): ModClickPreventionExtension
By default, clicking a node while holding the mod key will select the node. This extension disables that behavior.
@public
} from 'prosekit/extensions/mod-click-prevention'
import { defineParagraphfunction defineParagraph(): ParagraphExtension
@publicDefines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
} from 'prosekit/extensions/paragraph'
import { defineStrikefunction defineStrike(): StrikeExtension
@public
} from 'prosekit/extensions/strike'
import { defineTablefunction defineTable(): TableExtension
@public
} from 'prosekit/extensions/table'
import { defineTextfunction defineText(): TextExtension
@public
} from 'prosekit/extensions/text'
import { defineUnderlinefunction defineUnderline(): UnderlineExtension
@public
} from 'prosekit/extensions/underline'
import { defineVirtualSelectionfunction defineVirtualSelection(): VirtualSelectionExtension
Shows a virtual selection when the editor is not focused. When the editor is not focused, the selected inline content will be wrapped in a `<span>` element with the class `prosekit-virtual-selection`. This is useful when you want to move the focus to an element outside the editor, but still want to show the selection.
@public
} from 'prosekit/extensions/virtual-selection'
function defineBasicExtensionfunction defineBasicExtension(): Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, ListExtension, ... 17 more ..., PlainExtension]>() { return unionunion<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, ListExtension, ... 17 more ..., PlainExtension]>(exts_0: DocExtension, exts_1: TextExtension, exts_2: ParagraphExtension, exts_3: HeadingExtension, exts_4: ListExtension, exts_5: BlockquoteExtension, exts_6: ImageExtension, exts_7: HorizontalRuleExtension, exts_8: TableExtension, exts_9: CodeBlockExtension, exts_10: ItalicExtension, exts_11: BoldExtension, exts_12: UnderlineExtension, exts_13: StrikeExtension, exts_14: CodeExtension, exts_15: LinkExtension, exts_16: PlainExtension, exts_17: BaseCommandsExtension, exts_18: HistoryExtension, exts_19: PlainExtension, exts_20: PlainExtension, exts_21: PlainExtension, exts_22: PlainExtension): Union<...> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```@public
(
// Nodes defineDocfunction defineDoc(): DocExtension
@public
(),
defineTextfunction defineText(): TextExtension
@public
(),
defineParagraphfunction defineParagraph(): ParagraphExtension
@publicDefines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
(),
defineHeadingfunction defineHeading(): HeadingExtension
@public
(),
defineListfunction defineList(): ListExtension
@public
(),
defineBlockquotefunction defineBlockquote(): BlockquoteExtension
@public
(),
defineImagefunction defineImage(): ImageExtension
@public
(),
defineHorizontalRulefunction defineHorizontalRule(): HorizontalRuleExtension
@public
(),
defineTablefunction defineTable(): TableExtension
@public
(),
defineCodeBlockfunction defineCodeBlock(): CodeBlockExtension
Adds `codeBlock` nodes to the editor. This includes the following extensions: - {@link defineCodeBlockSpec } - {@link defineCodeBlockInputRule } - {@link defineCodeBlockEnterRule } - {@link defineCodeBlockKeymap } - {@link defineCodeBlockCommands } .
@public
(),
// Marks defineItalicfunction defineItalic(): ItalicExtension
@public
(),
defineBoldfunction defineBold(): BoldExtension
@public
(),
defineUnderlinefunction defineUnderline(): UnderlineExtension
@public
(),
defineStrikefunction defineStrike(): StrikeExtension
@public
(),
defineCodefunction defineCode(): CodeExtension
@public
(),
defineLinkfunction defineLink(): LinkExtension
@public
(),
// Others defineBaseKeymap
function defineBaseKeymap(options?: {
    priority?: Priority;
}): BaseKeymapExtension
Defines some basic key bindings.
@public
(),
defineBaseCommandsfunction defineBaseCommands(): BaseCommandsExtension
Add some base commands
@public
(),
defineHistoryfunction defineHistory({ depth, newGroupDelay, }?: HistoryOptions): HistoryExtension
Add undo/redo history to the editor.
@paramoptions@public
(),
defineDropCursorfunction defineDropCursor(options?: DropCursorOptions): DropCursorExtension
Show up a decoration at the drop position when something is dragged over the editor. See [prosemirror-dropcursor](https://github.com/ProseMirror/prosemirror-dropcursor) for more information.
@public
(),
defineGapCursorfunction defineGapCursor(): GapCursorExtension
Capture clicks near and arrow-key-motion past places that don't have a normally selectable position nearby, and create a gap cursor selection for them. The cursor is drawn as an element with class `ProseMirror-gapcursor`. You can either include `prosekit/extensions/gap-cursor.css` or add your own styles to make it visible. See [prosemirror-gapcursor](https://github.com/ProseMirror/prosemirror-gapcursor) for more information.
@public
(),
defineVirtualSelectionfunction defineVirtualSelection(): VirtualSelectionExtension
Shows a virtual selection when the editor is not focused. When the editor is not focused, the selected inline content will be wrapped in a `<span>` element with the class `prosekit-virtual-selection`. This is useful when you want to move the focus to an element outside the editor, but still want to show the selection.
@public
(),
defineModClickPreventionfunction defineModClickPrevention(): ModClickPreventionExtension
By default, clicking a node while holding the mod key will select the node. This extension disables that behavior.
@public
(),
) }

This compositional pattern allows you to build on existing extensions and add exactly the features you need. For example, if you want all the functionality of the basic extension plus syntax highlighting for code blocks, you can simply combine them:

import { defineBasicExtensionfunction defineBasicExtension(): BasicExtension
Define a basic extension that includes some common functionality. You can copy this function and customize it to your needs. It's a combination of the following extension functions: - {@link defineDoc } - {@link defineText } - {@link defineParagraph } - {@link defineHeading } - {@link defineList } - {@link defineBlockquote } - {@link defineImage } - {@link defineHorizontalRule } - {@link defineTable } - {@link defineCodeBlock } - {@link defineItalic } - {@link defineBold } - {@link defineUnderline } - {@link defineStrike } - {@link defineCode } - {@link defineLink } - {@link defineBaseKeymap } - {@link defineBaseCommands } - {@link defineHistory } - {@link defineDropCursor } - {@link defineGapCursor } - {@link defineVirtualSelection } - {@link defineModClickPrevention }
@public
} from 'prosekit/basic'
import { createEditorfunction createEditor<E extends Extension>(options: EditorOptions<E>): Editor<E>
@public
,
unionfunction union<const E extends readonly Extension[]>(...exts: E): Union<E> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```@public
,
} from 'prosekit/core' import { defineCodeBlockShikifunction defineCodeBlockShiki({ themes, langs, ...rest }?: CodeBlockShikiOptions): Extension
Adds syntax highlighting to code blocks using the [Shiki](https://github.com/shikijs/shiki) package. It will set two CSS variables on the code block elements: - `--prosemirror-highlight`: sets text color - `--prosemirror-highlight-bg`: sets background color
@paramoptions - The options to configure the Shiki highlighter.@public
} from 'prosekit/extensions/code-block'
function defineMyExtensionfunction defineMyExtension(): Union<readonly [BasicExtension, Extension<ExtensionTyping<any, any, any>>]>() { return unionunion<readonly [BasicExtension, Extension<ExtensionTyping<any, any, any>>]>(exts_0: BasicExtension, exts_1: Extension<ExtensionTyping<any, any, any>>): Union<...> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```@public
(
defineBasicExtensionfunction defineBasicExtension(): BasicExtension
Define a basic extension that includes some common functionality. You can copy this function and customize it to your needs. It's a combination of the following extension functions: - {@link defineDoc } - {@link defineText } - {@link defineParagraph } - {@link defineHeading } - {@link defineList } - {@link defineBlockquote } - {@link defineImage } - {@link defineHorizontalRule } - {@link defineTable } - {@link defineCodeBlock } - {@link defineItalic } - {@link defineBold } - {@link defineUnderline } - {@link defineStrike } - {@link defineCode } - {@link defineLink } - {@link defineBaseKeymap } - {@link defineBaseCommands } - {@link defineHistory } - {@link defineDropCursor } - {@link defineGapCursor } - {@link defineVirtualSelection } - {@link defineModClickPrevention }
@public
(),
defineCodeBlockShikifunction defineCodeBlockShiki({ themes, langs, ...rest }?: CodeBlockShikiOptions): Extension
Adds syntax highlighting to code blocks using the [Shiki](https://github.com/shikijs/shiki) package. It will set two CSS variables on the code block elements: - `--prosemirror-highlight`: sets text color - `--prosemirror-highlight-bg`: sets background color
@paramoptions - The options to configure the Shiki highlighter.@public
(),
) } const editorconst editor: Editor<Union<readonly [BasicExtension, Extension<ExtensionTyping<any, any, any>>]>> = createEditorcreateEditor<Union<readonly [BasicExtension, Extension<ExtensionTyping<any, any, any>>]>>(options: EditorOptions<Union<readonly [BasicExtension, Extension<ExtensionTyping<any, any, any>>]>>): Editor<...>
@public
({ extensionEditorOptions<Union<readonly [BasicExtension, Extension<ExtensionTyping<any, any, any>>]>>.extension: Union<readonly [BasicExtension, Extension<ExtensionTyping<any, any, any>>]>
The extension to use when creating the editor.
: defineMyExtensionfunction defineMyExtension(): Union<readonly [BasicExtension, Extension<ExtensionTyping<any, any, any>>]>() })

Since each extension is composed of smaller parts, you can easily customize functionality by picking exactly which components you want. For example, the defineCode() extension defines a mark spec, commands, keyboard shortcut, and input rule.

If you want to keep everything except the keyboard shortcut, you can inspect the source code of the defineCode() extension, and create a custom version:

import { unionfunction union<const E extends readonly Extension[]>(...exts: E): Union<E> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```@public
} from 'prosekit/core'
import { defineCodeCommandsfunction defineCodeCommands(): CodeCommandsExtension
@internal
,
defineCodeInputRulefunction defineCodeInputRule(): PlainExtension
@internal
,
defineCodeSpecfunction defineCodeSpec(): CodeSpecExtension
@internal
,
// Omitting defineCodeKeymap } from 'prosekit/extensions/code' function defineMyCodefunction defineMyCode(): Union<readonly [CodeSpecExtension, CodeCommandsExtension, PlainExtension]>() { return unionunion<readonly [CodeSpecExtension, CodeCommandsExtension, PlainExtension]>(exts_0: CodeSpecExtension, exts_1: CodeCommandsExtension, exts_2: PlainExtension): Union<...> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```@public
(
defineCodeSpecfunction defineCodeSpec(): CodeSpecExtension
@internal
(),
defineCodeCommandsfunction defineCodeCommands(): CodeCommandsExtension
@internal
(),
defineCodeInputRulefunction defineCodeInputRule(): PlainExtension
@internal
(),
// No keyboard shortcut ) }