Skip to content

Custom node views

Most nodes render fine via the toDOM you wrote in their spec. Reach for a node view when you need:

  • Interactive content inside the node (buttons, inputs).
  • Live data (an embed that fetches metadata).
  • Per-instance state ProseMirror's selection model can't express.

See Concepts → Views for the full mental model; this guide is the cookbook.

Every framework integration ships a define<Framework>NodeView:

FrameworkWrapper
ReactdefineReactNodeView
VuedefineVueNodeView
PreactdefinePreactNodeView
SveltedefineSvelteNodeView
SoliddefineSolidNodeView

If you don't use any of these frameworks, use the framework-agnostic defineNodeView which takes a ProseMirror NodeViewConstructor.

import { union } from 'prosekit/core'
import { defineImage } from 'prosekit/extensions/image'
import { defineReactNodeView, type ReactNodeViewProps } from 'prosekit/react'

function ImageView(props: ReactNodeViewProps) {
  const src = String(props.node.attrs.src ?? '')
  return (
    <img
      src={src}
      alt={String(props.node.attrs.alt ?? '')}
      data-selected={props.selected ? '' : undefined}
    />
  )
}

export function defineCustomImage() {
  return union(
    defineImage(),
    defineReactNodeView({
      name: 'image',
      component: ImageView,
    }),
  )
}

The selected prop comes from the editor selection. Use it to highlight the node when the user clicks on it.

props.setAttrs(partial) updates the node attributes. Combine with the Resizable component to persist drag-resize state:

import type { ReactNodeViewProps } from 'prosekit/react'
import { ResizableHandle, ResizableRoot } from 'prosekit/react/resizable'

function ResizableImageView(props: ReactNodeViewProps) {
  return (
    <ResizableRoot
      width={Number(props.node.attrs.width ?? 480)}
      onResizeEnd={({ width }) => props.setAttrs({ width })}
    >
      <img src={String(props.node.attrs.src ?? '')} />
      <ResizableHandle />
    </ResizableRoot>
  )
}
  • code-block-themes: a code block that lets you pick a syntax highlighting theme from a dropdown
  • image-view: custom image node views with resize support.