---
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-07
---

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 is in-place as long as every registered block tool implements `setReadOnly(state)` on its prototype — every bundled tool does — so the same editor instance flips modes, preserving caret position, undo history and scroll, and 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 → re-render cycle, which recreates all block instances and does not restore the caret (scroll is restored, and the undo history is deliberately left untouched).

[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 — no destroy/recreate: block instances, caret position, undo history and scroll are preserved — provided every registered block tool implements `setReadOnly(state)` on its prototype. The check is all-or-nothing, and a single tool without it (every bundled tool has one; a third-party tool may not) sends EVERY toggle down the fallback path — save → clear → re-render — which recreates all block instances and does not restore the caret, while 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/leave read-only mode — it toggles in place, preserving caret, undo history and scroll, as long as every registered block tool implements `setReadOnly()`. Returns a promise resolving 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. Sticky: `set()` writes `config.readOnly` only when you pass an actual boolean, so omitting the option keeps whatever `hideControls` is currently in effect — from the constructor config or an earlier `set()` call. Pass `{ hideControls: false }` explicitly to bring the controls back; `false` is only the effective value 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 parameter, toggles current state. With parameter, sets to specified state.

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

When to use

Kept for compatibility; without a parameter it flips the current state.

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`: this build of Blok implements 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 (which needs every block tool to implement `setReadOnly`, and is not exposed anywhere). Use it 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.
```
