---
title: "Blok Read-Only API — toggle read-only mode"
description: "Toggle read-only mode at runtime, and what a tool must implement for the switch to work in place."
source: https://blokeditor.com/docs/readonly-api/
lastmod: 2026-09-08
---

Framework JavaScript

Extending & system ReadOnly

On this page readOnly.set(state, options?)

# Read-only API: toggle editing on and off

Control the read-only state of the editor. Toggling happens in place as long as every registered block tool implements `setReadOnly(state)` on its prototype. Every bundled tool does.

The same editor instance then flips modes and keeps caret position, undo history and scroll. So an edit/view toggle is `readOnly.set(!isEditing)` on ONE instance, instead of destroying one editor and constructing another.

The check is all-or-nothing. Install a single block tool without `setReadOnly` and every toggle falls back to a save, clear and re-render cycle. That cycle recreates all block instances and does not restore the caret. Scroll is still restored, and the undo history is deliberately left untouched.

Last updated Sep 8, 2026 [Edit this page on GitHub](https://github.com/JackUait/blok/blob/main/docs/src/components/api/api-data.ts)

### Reaching the editor instance

The methods below run on the editor you created with new Blok(). They are available once editor.isReady resolves.

TypeScript

```
// You already hold the instance returned by the constructor.
const editor = new Blok({ holder: 'editor' });
await editor.isReady;

// Call any API method on it.
editor.caret.setToLastBlock('end');
```

## Methods

### readOnly.set(state, options?)

Promise<boolean>

Set read-only mode to the specified boolean state. The toggle happens in place, with no destroy or recreate step: block instances, caret position, undo history and scroll are all preserved.

That holds only if every registered block tool implements `setReadOnly(state)` on its prototype. The check is all-or-nothing.

One tool without it sends EVERY toggle down the fallback path of save, clear and re-render (every bundled tool has one, a third-party tool may not). That path recreates all block instances and does not restore the caret, though scroll and undo history still survive.

Pass `{ hideControls: true }` to also hide the hover toolbar, block settings and inline toolbar while read-only is active. The option writes the object form of `config.readOnly`, so the live state reflects it. Returns the new state.

When to use

The preferred way to enter or leave read-only mode. It toggles in place and preserves caret, undo history and scroll, as long as every registered block tool implements `setReadOnly()`. Returns a promise that resolves to the new state once applied.

Parameters

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `state` | `boolean` | Required | — | Read-only state to set. |
| `options.hideControls` | `boolean` | — | `unchanged (inherits the current `config.readOnly`)` | Hide all editor controls (hover toolbar, block settings popover, inline toolbar) while read-only is active. The value is sticky. `set()` writes `config.readOnly` only when you pass an actual boolean, so omitting the option keeps whatever `hideControls` is already in effect, from the constructor config or from an earlier `set()` call. Pass `{ hideControls: false }` explicitly to bring the controls back. `false` is the effective value only when `config.readOnly` was never given in object form. |

TypeScript

```
// The edit/view toggle: ONE instance, flipped in place —
// caret, undo history and scroll survive the switch
await editor.readOnly.set(!isEditing);

// Enable read-only and hide all controls
// (hover toolbar, block settings, inline toolbar)
await editor.readOnly.set(true, { hideControls: true });

// Check state
console.log(editor.readOnly.isEnabled); // true or false
console.log(editor.readOnly.togglesInPlace); // true
```

### readOnly.toggle(state?)

Promise<boolean>

Deprecated

Toggle read-only state. Without a parameter it toggles the current state. With a parameter it sets the specified state.

Use instead: [readOnly.set](#readonly-api-readonly-set)

When to use

Kept for compatibility. It flips the current state when called without a parameter.

TypeScript

```
// Toggle current state
const isReadOnly = await editor.readOnly.toggle();

// Enable read-only
await editor.readOnly.toggle(true);

// Disable read-only
await editor.readOnly.toggle(false);
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| `isEnabled` | `boolean` | Current read-only state |
| `togglesInPlace` | `true` | A build-level marker, hardcoded to `true`. It says this build of Blok has the in place toggle path, instead of always recreating the editor. It is not a capability probe. It does not report whether the currently installed tool set qualifies for that path. That path needs every block tool to implement `setReadOnly`, and it is not exposed anywhere. Use the marker only to detect a Blok build old enough to predate in-place toggling. |

TypeScript

```
// The edit/view toggle: one instance, one call.
// Caret, undo history and scroll survive the switch —
// no destroy-and-recreate.
async function setEditing(isEditing: boolean) {
  await editor.readOnly.set(!isEditing);
}

// Framework adapters do this for you: change the readOnly
// prop (React/Vue) or input (Angular) and the adapter calls
// readOnly.set on the existing instance — same editor identity.
```
