---
title: "Tutorial — Build a Notion-Style Editor with Blok"
description: "Step by step: mount the editor, register tools, persist blocks, and reload saved content into a fresh instance."
source: https://blokeditor.com/docs/tutorial/
lastmod: 2026-09-07
---

Framework JavaScript

Getting started Build your first editor

# Build your first Blok editor

Mount Blok, capture some content, and save it as JSON you can store and load back — the full round-trip in five steps.

Last updated Jun 30, 2026 [Edit this page on GitHub](https://github.com/JackUait/blok/blob/main/docs/src/components/api/TutorialContent.tsx)

This is a hands-on walkthrough. By the end you'll have a working editor, you'll have saved its content to JSON, and you'll have loaded that JSON straight back in — the round-trip that everything else in Blok is built on.

1

## Mount the editor

Point Blok at an element on your page and create an instance. Blok bundles no block tools, so register `paragraph` in the `tools` map — it is the tool the default block resolves to, and without it that first block renders a "This block cannot be displayed" stub instead of an editable line. Once `isReady` resolves you have a live editor with an empty paragraph waiting for the caret.

TypeScript

```
import { Blok } from '@bloklabs/core';
import { Paragraph } from '@bloklabs/core/tools';

const editor = new Blok({
  holder: 'editor', // the id of a <div> on your page
  tools: {
    paragraph: Paragraph, // the default block every empty editor starts with
  },
});

await editor.isReady;
```

2

## Write some content

Click in and type. Press Enter to start a new block, or press `/` to open the tool menu and choose a heading, list, or quote. Every line you create is a separate block.

3

## Save it to JSON

Call `save()` to get your content as a plain object. There's no HTML to parse and no hidden state — just blocks you can store anywhere.

TypeScript

```
const data = await editor.save();

console.log(data);
// {
//   time: 1719000000000,
//   blocks: [
//     { id: 'a1b2c3', type: 'paragraph', data: { text: 'Hello, Blok' } },
//   ],
//   version: '1.13.0', // the installed Blok package version
// }
```

That object is the whole point. It's portable JSON you can drop into a database, a file, or an API — and it's exactly what you'll load back next.

4

## Load it back

Hand the same object to `render()` and Blok rebuilds the content exactly. Save on unload, render on load — that's persistence, done.

TypeScript

```
// On the next page load, hand the same object back:
await editor.render(data);
```

5

## Add a tool

Blok ships lean — it registers no block tools of its own. Register the ones you want under the `tools` config and they appear in the `/` menu, ready to use. Keep `paragraph` in the map: there is no default set to fall back to, so every block type you rely on has to be listed, the default one included.

TypeScript

```
import { Blok } from '@bloklabs/core';
import { Header, List, Paragraph, Quote } from '@bloklabs/core/tools';

const editor = new Blok({
  holder: 'editor',
  tools: {
    paragraph: Paragraph, // keep the default block registered
    header: Header,
    list: List,
    quote: Quote,
  },
});
```

## Checkpoint: what success looks like

You should now have a live editor on the page, a `data` object in hand from `editor.save()` with your blocks inside it, and that same object loading cleanly through `editor.render()`. If all three are true, you've got the round-trip — everything else in Blok builds on it.

## Where to go next

[Create a custom block tool Build your own block from scratch — render, edit, and save like the built-ins.](https://blokeditor.com/docs/custom-block-tool/) [Everything is a block Understand the one idea the whole API is built on.](https://blokeditor.com/docs/concepts/)
