Skip to main content

Overview

ProseKit provides several APIs for defining custom extensions that add nodes, marks, and functionality to your editor. Extensions are the building blocks of ProseKit editors.

Node Extensions

defineNodeSpec()

Defines a node type into the editor schema.
function defineNodeSpec<Node extends string, Attrs extends AnyAttrs>(
  options: NodeSpecOptions<Node, Attrs>
): Extension<{ Nodes: { [K in Node]: Attrs } }>

Parameters

options
NodeSpecOptions<Node, Attrs>
required
Configuration for the node specification
options.name
string
required
The name of the node type
options.topNode
boolean
Whether this is the top-level node type. Only one node type can be the top-level node in a schema.
options.attrs
Record<string, AttrSpec>
The attributes that nodes of this type get
options.content
string
The content expression for this node type (e.g., “inline*”, “block+”)
options.group
string
The group or groups this node belongs to (e.g., “block”, “inline”)
options.parseDOM
ParseRule[]
Rules for parsing DOM content into this node
options.toDOM
(node: Node) => DOMOutputSpec
Function to serialize this node to DOM

Example

import { defineNodeSpec } from 'prosekit/core'

const fancyParagraph = defineNodeSpec({
  name: 'fancyParagraph',
  content: 'inline*',
  group: 'block',
  parseDOM: [{ tag: 'p.fancy' }],
  toDOM() {
    return ['p', { 'class': 'fancy' }, 0]
  },
})

defineNodeAttr()

Defines an attribute for a node type.
function defineNodeAttr<NodeType extends string, AttrName extends string, AttrType>(
  options: NodeAttrOptions<NodeType, AttrName, AttrType>
): Extension<{ Nodes: { [K in NodeType]: { [K in AttrName]: AttrType } } }>

Parameters

options
NodeAttrOptions
required
options.type
string
required
The name of the node type
options.attr
string
required
The name of the attribute
options.default
AttrType
The default value for this attribute
options.splittable
boolean
Whether the attribute should be kept when the node is split. Set to true to inherit the attribute when splitting the node by pressing Enter.
options.toDOM
(value: AttrType) => [string, string] | null
Returns the attribute key and value to be set on the HTML element
options.parseDOM
(node: HTMLElement) => AttrType
Parses the attribute value from the DOM

Example

import { defineNodeAttr } from 'prosekit/core'

const headingLevel = defineNodeAttr({
  type: 'heading',
  attr: 'level',
  default: 1,
  toDOM: (value) => ['data-level', String(value)],
  parseDOM: (node) => Number(node.getAttribute('data-level')) || 1
})

Mark Extensions

defineMarkSpec()

Defines a mark type into the editor schema.
function defineMarkSpec<Mark extends string, Attrs extends AnyAttrs>(
  options: MarkSpecOptions<Mark, Attrs>
): Extension<{ Marks: { [K in Mark]: Attrs } }>

Parameters

options
MarkSpecOptions<Mark, Attrs>
required
Configuration for the mark specification
options.name
string
required
The name of the mark type
options.attrs
Record<string, AttrSpec>
The attributes that marks of this type get
options.parseDOM
ParseRule[]
Rules for parsing DOM content into this mark
options.toDOM
(mark: Mark, inline: boolean) => DOMOutputSpec
Function to serialize this mark to DOM
options.inclusive
boolean
Whether this mark should be active when the cursor is at its end
options.excludes
string
The marks that this mark excludes
options.group
string
The group or groups this mark belongs to
options.spanning
boolean
Whether this mark should span multiple nodes

Example

import { defineMarkSpec } from 'prosekit/core'

const bold = defineMarkSpec({
  name: 'bold',
  parseDOM: [
    { tag: 'strong' },
    { tag: 'b' },
    { style: 'font-weight', getAttrs: value => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null },
  ],
  toDOM() {
    return ['strong', 0]
  },
})

defineMarkAttr()

Defines an attribute for a mark type.
function defineMarkAttr<MarkType extends string, AttrName extends string, AttrType>(
  options: MarkAttrOptions<MarkType, AttrName, AttrType>
): Extension<{ Marks: { [K in MarkType]: AttrType } }>

Parameters

options
MarkAttrOptions
required
options.type
string
required
The name of the mark type
options.attr
string
required
The name of the attribute
options.default
AttrType
The default value for this attribute
options.toDOM
(value: AttrType) => [string, string] | null
Returns the attribute key and value to be set on the HTML element
options.parseDOM
(node: HTMLElement) => AttrType
Parses the attribute value from the DOM

Example

import { defineMarkAttr } from 'prosekit/core'

const linkHref = defineMarkAttr({
  type: 'link',
  attr: 'href',
  default: '',
  toDOM: (value) => ['href', value],
  parseDOM: (node) => node.getAttribute('href') || ''
})

Command Extensions

defineCommands()

Defines custom commands for the editor.
import { defineCommands } from 'prosekit/core'

const myCommands = defineCommands({
  insertHello: () => {
    return (state, dispatch) => {
      const { tr } = state
      tr.insertText('Hello!')
      dispatch?.(tr)
      return true
    }
  },
  toggleCustomMark: (attrs?: Attrs) => {
    return toggleMark({ type: 'customMark', attrs })
  }
})

Extension Type Safety

ProseKit extensions are fully type-safe. When you define nodes, marks, or commands, TypeScript will infer the correct types throughout your editor:
import { createEditor, union } from 'prosekit/core'
import { defineMarkSpec } from 'prosekit/core'

const bold = defineMarkSpec({
  name: 'bold',
  parseDOM: [{ tag: 'strong' }],
  toDOM: () => ['strong', 0]
})

const italic = defineMarkSpec({
  name: 'italic',
  parseDOM: [{ tag: 'em' }],
  toDOM: () => ['em', 0]
})

const extension = union([bold, italic])
const editor = createEditor({ extension })

// TypeScript knows these marks exist
editor.marks.bold.create()
editor.marks.italic.create()

// TypeScript will error on non-existent marks
// editor.marks.underline.create() // Error!

See Also