Skip to content

Block handle

Block handles are floating controls that appear in the gutter next to whichever block currently contains the cursor or pointer. Use them to let users insert new blocks (+ button) or reorder existing ones (drag handle).

'use client'

import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'

import { createEditor, type NodeJSON } from 'prosekit/core'
import { ProseKit } from 'prosekit/react'
import { useMemo } from 'react'

import { sampleContent } from '../../sample/sample-doc-block-handle'
import { BlockHandle } from '../../ui/block-handle'
import { DropIndicator } from '../../ui/drop-indicator'

import { defineExtension } from './extension'

interface EditorProps {
  initialContent?: NodeJSON
}

export default function Editor(props: EditorProps) {
  const defaultContent = props.initialContent ?? sampleContent
  const editor = useMemo(() => {
    const extension = defineExtension()
    return createEditor({ extension, defaultContent })
  }, [defaultContent])

  return (
    <ProseKit editor={editor}>
      <div className="box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow-sm flex flex-col bg-[canvas] text-black dark:text-white">
        <div className="relative w-full flex-1 box-border overflow-y-auto">
          <div ref={editor.mount} className="ProseMirror box-border min-h-full px-[max(4rem,calc(50%-20rem))] py-8 outline-hidden outline-0 [&_span[data-mention=user]]:text-blue-500 [&_span[data-mention=tag]]:text-violet-500"></div>
          <BlockHandle />
          <DropIndicator />
        </div>
      </div>
    </ProseKit>
  )
}
npx shadcn@latest add @prosekit/react-ui-block-handle
BlockHandleRoot                  # root component
└── BlockHandlePositioner        # anchors the popup to the gutter 
    └── BlockHandlePopup         # the floating container
        ├── BlockHandleAdd       # "+" button that inserts a new block below
        └── BlockHandleDraggable # "⋮⋮" drag handle that reorders by dragging

You can keep both BlockHandleAdd and BlockHandleDraggable, or only one. The popup is just the visible container. Pair this with the Drop indicator component to draw a horizontal line where the dragged block will land.

  • Drop indicator: companion overlay that shows the drop target during a drag.
  • Table handle: sibling primitive for table-row and table-column selection.