DOCUMENTATION

Code Block

Last updated May 2026
Advanced Block
All plans

A full code editor with syntax highlighting, language selection, line wrapping, undo/redo history, and a draggable resize handle. Built on CodeMirror 6 — a single unified editor layer with no rendering hacks. Add with /code or /snippet.

Code Block — dark theme, TypeScript
typescriptCopy
const applyPreset = async (id: string) => {
const tokens = applyBrandingPreset(id)
if (!tokens) return
await saveToDocument(tokens)
}

Adding a Code Block

/ → Code
Type / on any empty paragraph, type code or snippet, press Enter.
``` shortcut
Type ``` at the start of an empty block and press Space or Enter. The fence is removed and the block converts to a Code Block.
Block palette
Click + on any block → scroll to Advanced → Code Block.

The editor

The Code Block uses CodeMirror 6 — a single unified editor that handles syntax highlighting, cursor rendering, selection, paste, and text input in one layer. There is no dual-layer transparent textarea trick; CodeMirror's own content-editable DOM is the editor surface.

The editor mounts once on load. Theme and language changes after mount use CodeMirror's Compartment system — they hot-swap the relevant extension without destroying and recreating the editor or losing the undo history.

Keyboard behaviour

Tab
Inserts two spaces at the cursor. Does not move focus out of the code block — standard browser tab behaviour is suppressed inside the editor.
Escape
Blurs the CodeMirror editor and returns keyboard focus to the block editor, so arrow keys navigate between blocks again.
↑ on first line
Moves focus to the block above the code block in the document.
↓ on last line
Moves focus to the block below the code block in the document.
⌘Z / Ctrl+Z
Undo within the code block. CodeMirror maintains its own undo history independent of the document-level undo.
Copy button
Copies the full raw text of the editor to the clipboard. The button label changes to "Copied!" for 2 seconds.
Arrow key navigation out of the code block (↑ on line 1, ↓ on last line) works by dispatching a synthetic KeyboardEvent on the container, which the block editor's own keydown handler picks up and treats as a normal inter-block navigation event.

Languages

23 languages are available from the dropdown in the toolbar header. Language extensions are loaded lazily — only the language you select is fetched. Switching languages hot-swaps the parser via CodeMirror's Compartment API without remounting the editor.

Available languages: plaintext, JavaScript, TypeScript, JSX, TSX, Python, Rust, Go, Java, C, C++, C#, Ruby, PHP, Swift, Kotlin, SQL, HTML, CSS, JSON, YAML, Bash, Markdown.

Six of these languages appear in the dropdown but have no syntax highlighting implemented — they render as plain text: Go, C#, Ruby, Swift, Kotlin, and Bash. Everything else on the list highlights normally.
JSX and TSX are separate language options from JavaScript and TypeScript. If your snippet includes JSX syntax (<Component />), select JSX or TSX for correct highlighting.

Syntax highlighting

Two themes are available, selected automatically based on the document's background color:

Dark (one-dark)
Applied when the document background is dark. Uses the One Dark theme — a warm dark palette with purple, orange, and teal token colors.
Light (default)
Applied when the document background is light. Uses CodeMirror's built-in default highlight style — muted blues and greens on a near-white background.

Theme switching is reactive — if you change the document background color in the toolbar, the code block theme updates immediately via the Compartment system.

Resizing

The block has a drag handle at the bottom — five dots indicating the resize zone. Drag up to shrink, drag down to grow. Minimum height is 80px. Default height is 180px.

Unlike the previous implementation, height is now persisted to block metadata (metadata.codeBlockHeight). The block restores to its saved height on remount, page reload, and across collaborative sessions.

The resize drag is handled with raw mousemove / mouseup listeners on window rather than the element, so dragging outside the block boundary doesn't drop the resize handle mid-drag.

Content sync

The editor debounces content saves by 600ms — the block's onChange is only called 600ms after the last keystroke. This keeps the Yjs CRDT document and autosave system from being flooded with updates while typing.

External updates — from the AI panel, Yjs real-time collaboration, or version history restore — are applied to the editor via a full document replacement dispatch. The sync logic uses two refs (lastSentRef and lastExternalRef) to distinguish genuine external updates from echoes of the user's own saves, preventing cursor jumps during normal typing.

Publishing

On the published page the code block renders as a dark panel with a header bar showing the language name. The code is displayed in a <pre> tag with monospace font and horizontal scrolling enabled. Syntax highlighting is not applied on the published page — the raw code is shown with consistent dark styling.

Metadata reference

language
Stored in metadata.language. Defaults to plaintext. Controls which CodeMirror language extension is loaded and displayed in the toolbar dropdown.
codeBlockHeight
Stored in metadata.codeBlockHeight as a number (pixels). Set when the user releases the drag handle. Defaults to 180 if absent.
content
Plain text stored in block.content. HTML tags are stripped before passing to CodeMirror on mount.