Block Editor

A composable block-based editor. Content is a flat array of typed blocks — each block type is a regular Svelte component. Supports drag-to-reorder, block type switching, undo/redo, and keyboard navigation between blocks.


Demo

Drag the grip handle to reorder blocks. Use the buttons below to add new blocks. Shift+Enter splits a Markdown block, Backspace at the start merges with the previous block. ⌘Z / ⌘⇧Z undo/redo structural changes.

Interactive demo

This editor supports Markdown, tables, diagrams, YouTube embeds, quotes, and dividers. Blocks can be reordered by dragging the grip handle on the left.

You can drag blocks to reorder them using the grip handle on the left. Press Shift+Enter inside a Markdown block to split it.

A
B
C
D
1
2
$
$
$
3
$
$
$
4
$
$
$
5
$
$
$
RequestValidateProcessResponse

svelte.dev

tailwindcss.com

Svelte 5 Runes Explained

Built-in block types

All built-in block components are exported from @atom-forge/ui and can be used individually or combined. Import only the components you need and pass them in the components map.

Type keyComponentdata shapeJoinable
textBlockEditTextarea{ text: string }yes
codeBlockEditCode{ files: { filename, language, source: string }[]; title: string; open: boolean }—
linkBlockEditLinkCard{ url, title, description, favicon: string }—
markdownBlockEditMarkdown{ text: string }yes
headingBlockEditHeading{ text: string; level: 1|2|3 }—
quoteBlockEditQuote{ text: string }—
dividerBlockEditDividernull—
youtubeBlockEditYoutube{ url: string; title?: string }—
tableBlockEditTableTableData—
diagramBlockEditDiagramDiagramData—
calloutBlockEditCallout{ icon, color, title, text: string }—

Keyboard shortcuts

Shift+Enter — split the current joinable block at the cursor into two.
Backspace at start — merge with the previous block (if same joinable type) or focus it.
Delete at end — merge the next block into the current one.
ArrowUp / ArrowDown at edges — move focus to the previous/next block.
Tab / Shift+Tab — navigate between blocks in Markdown mode.


Custom block types

Any Svelte component with id: string and data: any props can be a block. Call api.register(id, controller) in an $effect to opt into focus management. Use api.updateData(id, newData) to persist changes back to the block array. Import getBlockAPI from @atom-forge/ui and call it during component initialization. Unregister the controller when the block is destroyed.