Editor

Editor

The full block editor over a `Block[]` document: Enter/Backspace/Tab semantics, markdown shortcuts, the `/` slash menu, the hover `+` and ⋮⋮ handle with the block menu, drag to reorder, and the selection text toolbar.

•
•

Keyboard

KeyBehavior
EnterSplit the block at the caret. Lists, to-dos (unchecked) and toggles (closed) continue their type; everything else continues as Text. At the very start of a non-empty block, inserts an empty block above. In an open toggle with children, the new block becomes its first child.
Enter (empty list item)Outdent if nested, otherwise turn the item into Text.
Shift+EnterSoft line break inside the block (Enter always does this in a Code block).
Backspace (at start)Non-text block → Text; nested block → outdent; previous block is void (divider, image, bookmark, table, equation, TOC, columns) → remove it; otherwise merge into the previous block (its children move up).
Delete (at end)Merge the next block into this one.
Tab / Shift+TabIndent into the previous sibling / outdent.
↑ / ↓On the first / last line, move to the previous / next block.
← / →At the start / end, move to the previous / next block.
⌘B / ⌘I / ⌘UToggle bold / italic / underline on the selection.
⌘EToggle inline code.
⌘⇧S (or ⌘⇧X)Toggle strikethrough.
⌘DDuplicate the block.
/Open the slash menu (at the start of a block or after a space). Typing filters; ↑ ↓ move; Enter or Tab insert; Esc or two spaces close.
Title: Enter / ↓Move from the page title to the first block (creates one if empty).
Handle: Enter / SpaceOpen the block action menu from the focused ⋮⋮ handle.

Markdown shortcuts

Typed at the start of a Text block, followed by a space (except ``` and ---`). The prefix is removed and the block converts in place, keeping the rest of its text.

TypeBecomes
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
- Bulleted list (also `* ` and `+ `)
1. Numbered list (any number)
[] To-do (also `[ ] `)
[x] Checked to-do
> Toggle list (open)
" Quote
```Code block (JavaScript)
---Divider, with a new Text block below

Slash menu, handle, drag and toolbar

  • Slash menu: typing / opens SlashMenu under the caret (above it if there is no room). Picking an item on an empty block converts it; otherwise a new block is inserted below. Items without an implementation (AI Meeting Notes, HTML, Video, Audio, File) just close the menu.
  • Hover handle: hovering a block shows + and ⋮⋮ 52px to its left, aligned to the first text line. + inserts a block below and opens the slash menu; Option/Alt-click inserts above.
  • Block menu: clicking ⋮⋮ opens BlockActionMenu (Turn into, Color, Copy link to block, Duplicate, Delete…) and highlights the block with a blue selection halo.
  • Drag: pressing ⋮⋮ and moving more than 4px drags the block; a 4px blue indicator shows the drop position (before / after the hovered block).
  • Text toolbar: selecting text in any block except Code (and the title) shows TextToolbar above the selection: marks, link, color, turn into.
  • Clicking below the last block focuses it, or appends a new Text block.

Usage

  • Use Editor for free-form documents people write and restructure (notes, specs, wiki pages).
  • Use it controlled (value + onChange) when you save the document; defaultValue for scratch content.
  • Persist the Block[] yourself; undo and redo use the browser’s native editing history only.
  • Don’t use Editor for short single-line or form input; use TextInput or Textarea instead.
  • Don’t use it to display content read-only; use BlockRenderer.

Installation

Install the package (React 19):

terminal
npm install @krackedui/motion

Import the styles once, in your app entry or root layout:

/src/main.tsx
import '@krackedui/motion/styles.css';

Use it anywhere inside your app's <Theme> (see Installation):

/src/EditorExample.tsx
import { useState } from 'react';
import { Editor, rt, type Block } from '@krackedui/motion';

const initial: Block[] = [
  { id: 'e1', type: 'heading_1', text: rt('Try the editor') },
  { id: 'e2', type: 'text', text: [{ text: 'Type ' }, { text: '/', marks: { code: true } }, { text: ' for commands, select text for the toolbar, hover a block for the handle.' }] },
  { id: 'e3', type: 'bulleted_list', text: rt('Press Tab to nest this item') },
  { id: 'e4', type: 'bulleted_list', text: rt('Enter continues the list') },
  { id: 'e5', type: 'to_do', text: rt('Check me'), props: { checked: false } },
  { id: 'e6', type: 'toggle', text: rt('Open this toggle'), props: { open: false }, children: [{ id: 'e6a', type: 'text', text: rt('Nested content') }] },
  { id: 'e7', type: 'text', text: [] },
];

export function EditorDemo() {
  const [doc, setDoc] = useState<Block[]>(initial);
  // The hover handle sits 52px left of the blocks: leave room for it.
  return (
    <div className="ps-14">
      <Editor value={doc} onChange={setDoc} />
    </div>
  );
}

Examples

With a page title

title renders the 40px page title above the blocks; Enter or ↓ in the title moves into the document. The title is read once (initial value); listen with onTitleChange.

•
•

Uncontrolled, empty

With no value the editor starts with one empty Text block that shows the focused placeholder.

API Reference

Editor

PropTypeDefault
value
Block[]
defaultValue
Block[]
onChange
(blocks: Block[]) => void
title
RichText
onTitleChange
(title: RichText) => void
placeholder
string
EDITOR_PLACEHOLDER
lastEditedBy
string
"You"
className
string
style
CSSProperties

Utilities

Exported alongside the editor.

PropTypeDefault
newBlock
(kind: BlockKind, text?: RichText) => Block
blockTree
namespace
blockIds
(blocks: Block[]) => string[]
EDITOR_PLACEHOLDER
string

Accessibility

  • Each block’s text is a contenteditable leaf; all editing works from the keyboard (see the table above).
  • The ⋮⋮ handle is a button labeled “Drag to move, click to open menu”; Enter / Space open the block menu. Dragging is pointer-only; use the block menu or Tab / Shift+Tab to restructure from the keyboard.
  • The + button is labeled “Click to add below. Option-click to add a block above”.
  • The slash menu is a dialog labeled “Slash commands”; focus stays in the text while ↑ ↓ move the highlight.
  • The hover handle only appears on mouse hover; keyboard users reach block actions through the slash menu, the shortcuts and the text toolbar.
© 2026 Motion. Internal documentation.