DraggableBlockExtension

Intuitive drag-and-drop for content blocks

Transform your editor with smooth, professional drag-and-drop functionality. Rearrange paragraphs, images, and any content blocks with visual feedback and animations.

Drag & DropBlock MovementReactive State

Key Features

Everything you need for seamless block rearrangement in your editor.

Visual Drag Handles

Hover over blocks to reveal drag handles. Click and drag to move content with smooth visual feedback.

Multiple Movement Options

Drag blocks directly, use up/down buttons, or even drag via text selection for flexible content rearrangement.

Reactive State System

Real-time drag state updates without polling. Access `activeStates.isDragging` for immediate feedback on drag operations.

Quick Start

Get drag-and-drop working in your editor in just a few lines.

Basic Setup

Import and add DraggableBlockExtension

function MyEditor() {
  return (
    <DefaultTemplate
      extensions={[draggableBlockExtension]}
      onReady={(editor) => {
        console.log('Editor with drag-and-drop ready!')
      }}
    />
  )
}
With Configuration

Customize drag behavior

const extensionsWithDraggable = [
  draggableBlockExtension.configure({
    showMoveButtons: true,        // Show up/down buttons
    showUpButton: true,           // Enable move up button
    showDownButton: true,         // Enable move down button
    buttonStackPosition: 'left',  // Position buttons on left
    enableTextSelectionDrag: true, // Allow dragging via text selection
    theme: {
      handle: 'my-drag-handle',
      handleActive: 'my-drag-handle-active',
      blockDragging: 'my-block-dragging',
      dropIndicator: 'my-drop-indicator'
    }
  }),
  historyExtension
] as const

Try DraggableBlockExtension

Interactive demo showcasing drag-and-drop features. Hover over blocks, grab handles, and rearrange content!

DraggableBlockExtension Demo

Try dragging blocks, using move buttons, and text selection drag

Dragging: Nope
Hover over blocks to see drag handles, then drag to rearrange content! Try selecting text and dragging too.

Reactive State System

Access drag state reactively without performance-killing polling.

Real-time Drag State

Access reactive drag state

function MyToolbar() {
  const { commands, activeStates } = useEditor()

  // Reactive state - updates immediately when dragging starts/stops
  const isDragging = activeStates?.isDragging || false

  return (
    <div className="toolbar">
      <button onClick={() => commands.moveCurrentBlockUp()}>
        Move Up
      </button>
      <button onClick={() => commands.moveCurrentBlockDown()}>
        Move Down
      </button>

      {/* Real-time drag state indicator */}
      <div className="drag-status">
        Dragging: {isDragging ? 'Yes' : 'No'}
      </div>
    </div>
  )
}

The activeStates.isDragging updates immediately when drag operations start and stop, providing real-time feedback without any polling.

Configuration Options

Customize every aspect of the drag-and-drop experience.

OptionTypeDescription
showMoveButtonsbooleanShow up/down arrow buttons for manual block movement.
showUpButtonbooleanEnable the move up button specifically.
showDownButtonbooleanEnable the move down button specifically.
buttonStackPosition'left' | 'right'Position of the move buttons relative to blocks.
enableTextSelectionDragbooleanAllow dragging blocks by selecting text within them.
themeobjectCSS class names for customizing handles, indicators, and animations.
handleRendererfunctionCustom renderer for drag handles with full control.
buttonsRendererfunctionCustom renderer for move up/down buttons.
dropIndicatorRendererfunctionCustom renderer for the drop indicator line.
Usage Examples

Custom styling with theme classes

.my-drag-handle {
  opacity: 0;
  transition: opacity 0.2s ease;
  cursor: grab;
}

.my-drag-handle:hover {
  opacity: 1;
}

.my-drag-handle-active {
  opacity: 1;
  cursor: grabbing;
}

.my-block-dragging {
  opacity: 0.5;
  transform: rotate(5deg);
}

.my-drop-indicator {
  height: 2px;
  background: #007acc;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.my-drop-indicator.active {
  opacity: 1;
}

Custom handle renderer for full control

function MyEditor() {
  const handleDragStart = () => {
    console.log('Drag started')
  }

  const handleDrop = (sourceKey: string, targetKey: string) => {
    console.log('Block moved from', sourceKey, 'to', targetKey)
  }

  const extensions = [
    draggableBlockExtension.configure({
      // Custom renderers for full control
      handleRenderer: ({ rect, isDragging, onDragStart }) => (
        <div
          style={{
            position: 'absolute',
            left: rect.left - 20,
            top: rect.top + rect.height / 2 - 10,
            width: 16,
            height: 16,
            background: isDragging ? '#007acc' : '#ccc',
            cursor: 'grab'
          }}
          onMouseDown={onDragStart}
        >
          ⋮⋮
        </div>
      )
    })
  ] as const

  return <DefaultTemplate extensions={extensions} />
}

API Reference

Commands and state queries for programmatic control.

Commands
moveBlock(sourceKey, targetKey, insertAfter)

Programmatically move a block to a new position.

moveCurrentBlockUp()

Move the currently selected block up one position.

moveCurrentBlockDown()

Move the currently selected block down one position.

Active States
isDragging

Real-time boolean indicating if a drag operation is in progress.

Programmatic Control

Using commands programmatically

function MyEditor() {
  const { commands, activeStates } = useEditor()

  const handleMoveBlock = () => {
    // Move a specific block programmatically
    commands.moveBlock('block-1', 'block-2', true)
  }

  const handleMoveCurrent = () => {
    // Move the currently selected block
    commands.moveCurrentBlockUp()
    // or
    commands.moveCurrentBlockDown()
  }

  return (
    <div>
      <button onClick={handleMoveBlock}>
        Move Block 1 After Block 2
      </button>
      <button onClick={handleMoveCurrent}>
        Move Current Block Up
      </button>
    </div>
  )
}

Best Practices

Tips for the best drag-and-drop experience.

Visual Feedback

Always provide clear visual cues like handles and drop indicators to guide users through the drag process.

Performance

The reactive state system ensures optimal performance without polling. Animations are hardware-accelerated for smooth 60fps experience.

Accessibility

Ensure drag handles are keyboard accessible and provide alternative navigation methods for users who can't use a mouse.

User Experience

Test with real users to ensure the drag interaction feels natural and intuitive in your specific use case.