A workspace you can build on.
OpenBook is a CRDT document model with a pluggable block system, signed plugins, an MCP server, and a self-hostable store. Everything the app does, your code can do too.
// A page is an array of blocks. Reactive blocks are just
// blocks with a registered renderer — the core knows nothing
// about them. A slider publishes a named value; a chart reads it.
[
{ "id": "h", "type": "heading", "props": { "level": 2 },
"text": [{ "t": "Compound growth" }] },
{ "id": "months", "type": "slider",
"props": { "name": "months", "value": 120, "min": 1, "max": 360 } },
{ "id": "chart", "type": "kitchart",
"props": { "kind": "line", "source":
"{series: [{name:'7%', data: Array.from({length: months}, (_, i) => Math.pow(1.07, i/12))}]}"
} }
]That JSON on the left is the document on the right — live. Drag the slider.
Primitives, not a framework
Small, composable pieces: a CRDT doc, a block registry, a scope that recomputes, a signed plugin loader, and a thin store.
CRDT documents (Yjs)
Every doc is a Y.Doc. Blocks are Y.Maps; text is Y.Text. Merges are conflict-free across tabs, peers and offline edits.
Block registry
registerCustomBlock() adds a type + renderer + slash command. The core editor is pure UI over the doc — it knows nothing about your blocks.
Reactive scope
Named inputs publish values; formulas and charts evaluate expressions over that scope and recompute on every change.
Signed plugins
Ship TS source + a manifest, zipped and signed with Ed25519. A sucrase loader registers it at runtime. Signing proves provenance.
MCP server
Drive OpenBook from an AI agent: read, create and edit pages over the Model Context Protocol.
Self-hostable store
A small Postgres-backed page store with a documented HTTP API. Bring your own database.
Write a block, ship a plugin
The artifact kit is built on the same public API your plugins use. No private hooks.
import {api} from '@book.dev/plugin-sdk';
// A plugin is TS source + an openbook.json manifest, zipped and
// Ed25519-signed. Its entry default-exports activate(api); the
// loader (sucrase + CJS) runs it and registers your blocks at
// runtime — the same registry the built-in kit uses.
export default function activate(a: typeof api) {
a.blocks.register({
type: 'sparkline', // registered as "<pluginId>/sparkline"
render: ({block, editor}) => <Sparkline block={block} editor={editor} />,
slash: {
label: 'Sparkline',
hint: 'A tiny inline chart over a named input',
keywords: 'spark mini chart trend',
make: () => ({type: 'sparkline', props: {source: '[1,2,3,5,8]'}}),
},
});
}Documents as data, in and out
Build documents in code with a deterministic CRDT seed, then export them to Markdown, HTML or a self-contained interactive deck. Migrate legacy content in too.
import {createSeededBlockDoc, blocksToMarkdown} from '@book.dev/ui';
// Build a document programmatically (deterministic CRDT seed),
// then render it to Markdown — or HTML, or a slide deck.
const doc = createSeededBlockDoc([
{type: 'heading', text: 'Release notes', props: {level: 1}},
{type: 'todo', text: 'Cut the v2 build'},
]);
const md = blocksToMarkdown(doc);From a CLI to your own server
Seed sample data, drive it from an agent, or host the store yourself.
CLI
Seed the reactive sample, back up and restore, and script your workspace.
MCP
Point an MCP-capable agent at OpenBook and let it author pages directly.
Postgres store
Run the page store against your own database; the API is documented and small.
Read the code, then bend it
It's all there: the CRDT model, the block registry, the plugin loader, the SDK.