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.
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.
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 | $ | $ | $ |
svelte.dev
tailwindcss.com
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 key | Component | data shape | Joinable |
|---|---|---|---|
| text | BlockEditTextarea | { text: string } | yes |
| code | BlockEditCode | { files: { filename, language, source: string }[]; title: string; open: boolean } | — |
| link | BlockEditLinkCard | { url, title, description, favicon: string } | — |
| markdown | BlockEditMarkdown | { text: string } | yes |
| heading | BlockEditHeading | { text: string; level: 1|2|3 } | — |
| quote | BlockEditQuote | { text: string } | — |
| divider | BlockEditDivider | null | — |
| youtube | BlockEditYoutube | { url: string; title?: string } | — |
| table | BlockEditTable | TableData | — |
| diagram | BlockEditDiagram | DiagramData | — |
| callout | BlockEditCallout | { icon, color, title, text: string } | — |
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.
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.