KQ-1309 · BLOCK/EDITOR
Block Editor
A document of text, heading, to-do and quote blocks, each a plain auto-growing textarea underneath, whose inline marks are plain text that renders formatted while the block rests. A block's grip drags it 1:1 with a lift on flick and a rubber band past the ends while its neighbours glide out of its way, and on release every block FLIPs on glide from where it was drawn to where the new order puts it, the dropped one carrying its release velocity; Alt+Up and Alt+Down make the same move with focus and caret kept. `/` opens a block menu at the caret on snap, a selection raises a formatting bubble over itself on snap, and Enter splits, Backspace joins and Up and Down cross between blocks at their first and last lines.
Field notes · North basin survey
Fieldline · shared with the survey team
9 blocks · 87 words
Alt+Up or Alt+Down moves the block. Type / for block types. Select text for formatting, or press Alt+F10 to reach it.
9 blocks · 87 words · 1 of 3 to-dos done
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/block-editor
Props
- blocks / defaultBlocks
- EditorBlock[]
- default defaultEditorBlocks
- The document: each block's id, type (paragraph, heading, subheading, todo, quote), text and, for a to-do, its tick. Controlled or uncontrolled.
- onBlocksChange
- (blocks: EditorBlock[]) => void
- default —
- Fires from the keystroke, drag, menu pick or tick that changed the document, with every block.
- title
- string
- default "Field notes · North basin survey"
- The document's name: the surface's heading and accessible name.
- meta
- string
- default "Fieldline · shared with the survey team"
- One quiet line under the title.
- placeholder
- string
- default "Type “/” for blocks"
- What an empty block says while you are in it.
- handles
- "hover" | "always" | "off"
- default "hover"
- The gutter's grip and + button: on hover and focus (faintly always on a phone), always, or none. Alt+Up and Alt+Down move blocks either way.
- slash
- "list" | "grid" | "off"
- default "list"
- The block menu that `/` and + open: rows with hints, a grid of tiles, or none (`/` is just a character; the markdown openings # , ## , [] and > still work).
- bubble
- "full" | "compact" | "off"
- default "full"
- The toolbar over a selection: bold, italic, code, strike and link; the first three; or none (the shortcuts still work).
- status
- "ready" | "loading" | "error"
- default "ready"
- Loading draws placeholder lines; error offers Retry.
- onRetry
- () => void
- default —
- The Retry button of the error state.
- onFocusChange
- (blockId: string | null) => void
- default —
- Fires with the block being written in, or null when focus leaves the document.
- readOnly
- boolean
- default false
- Reads without writing, moving or ticking; the gutter is hidden.
- label
- string
- default —
- The region's accessible name, when it should differ from the title.
- sound
- boolean
- default false
- Play a tick on menu moves, drag slot changes and Alt-moves, and a click on drops, picks, ticks and formats. Off unless asked for.
- disabled
- boolean
- default false
- Shows the document dimmed and takes no input.
- className
- string
- default —
- Classes for the root. The root is h-[560px] by default; pass a height to change it.
| Prop | Type | Default | Description |
|---|---|---|---|
| blocks / defaultBlocks | EditorBlock[] | defaultEditorBlocks | The document: each block's id, type (paragraph, heading, subheading, todo, quote), text and, for a to-do, its tick. Controlled or uncontrolled. |
| onBlocksChange | (blocks: EditorBlock[]) => void | — | Fires from the keystroke, drag, menu pick or tick that changed the document, with every block. |
| title | string | "Field notes · North basin survey" | The document's name: the surface's heading and accessible name. |
| meta | string | "Fieldline · shared with the survey team" | One quiet line under the title. |
| placeholder | string | "Type “/” for blocks" | What an empty block says while you are in it. |
| handles | "hover" | "always" | "off" | "hover" | The gutter's grip and + button: on hover and focus (faintly always on a phone), always, or none. Alt+Up and Alt+Down move blocks either way. |
| slash | "list" | "grid" | "off" | "list" | The block menu that `/` and + open: rows with hints, a grid of tiles, or none (`/` is just a character; the markdown openings # , ## , [] and > still work). |
| bubble | "full" | "compact" | "off" | "full" | The toolbar over a selection: bold, italic, code, strike and link; the first three; or none (the shortcuts still work). |
| status | "ready" | "loading" | "error" | "ready" | Loading draws placeholder lines; error offers Retry. |
| onRetry | () => void | — | The Retry button of the error state. |
| onFocusChange | (blockId: string | null) => void | — | Fires with the block being written in, or null when focus leaves the document. |
| readOnly | boolean | false | Reads without writing, moving or ticking; the gutter is hidden. |
| label | string | — | The region's accessible name, when it should differ from the title. |
| sound | boolean | false | Play a tick on menu moves, drag slot changes and Alt-moves, and a click on drops, picks, ticks and formats. Off unless asked for. |
| disabled | boolean | false | Shows the document dimmed and takes no input. |
| className | string | — | Classes for the root. The root is h-[560px] by default; pass a height to change it. |