Skip to content
FrameworkJavaScript

useBlokReady(): scoped editor readiness

Live readiness of the Blok editors inside a DOM subtree, as a boolean you can render from — the useBlokReady(options) hook in @bloklabs/react, the useBlokReady(options) composable in @bloklabs/vue (returns a ref), and injectBlokReady(options) in @bloklabs/angular (returns a signal). All three wrap the same core registry behind Blok.readyState() and Blok.subscribeReady(), so they cannot drift. It answers the question a comments list or a form actually has: are MY editors ready? Scope it with the ref you already hold on the container, so an unrelated editor elsewhere on the page cannot hold your gate closed. It is a live signal, not a one-shot latch: an editor mounted later re-closes the gate, and with settleOn: 'rendered' so does every re-render from a changed data prop. A scope holding no editors is ready, so the empty-list case needs no special-casing. It starts false and takes its first real reading once the scope element is attached (React: the mount effect; Vue: onMounted; Angular: afterNextRender), and a scope you asked for that has not resolved yet reports false rather than silently falling back to the whole page — over-waiting is safe, under-waiting is a bug.

Last updated Jul 22, 2026Edit this page on GitHub

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

useBlokReady(options?)

boolean

Live readiness of the Blok editors inside a DOM subtree, as a boolean you can render from — the useBlokReady(options) hook in @bloklabs/react, the useBlokReady(options) composable in @bloklabs/vue (returns a ref), and injectBlokReady(options) in @bloklabs/angular (returns a signal). All three wrap the same core registry behind Blok.readyState() and Blok.subscribeReady(), so they cannot drift. It answers the question a comments list or a form actually has: are MY editors ready? Scope it with the ref you already hold on the container, so an unrelated editor elsewhere on the page cannot hold your gate closed. It is a live signal, not a one-shot latch: an editor mounted later re-closes the gate, and with settleOn: 'rendered' so does every re-render from a changed data prop. A scope holding no editors is ready, so the empty-list case needs no special-casing. It starts false and takes its first real reading once the scope element is attached (React: the mount effect; Vue: onMounted; Angular: afterNextRender), and a scope you asked for that has not resolved yet reports false rather than silently falling back to the whole page — over-waiting is safe, under-waiting is a bug.

Parameters

ParameterTypeRequiredDefaultDescription
options.withinRefObject<Element | null> | Element | nullLive readiness of the Blok editors inside a DOM subtree, as a boolean you can render from — the useBlokReady(options) hook in @bloklabs/react, the useBlokReady(options) composable in @bloklabs/vue (returns a ref), and injectBlokReady(options) in @bloklabs/angular (returns a signal). All three wrap the same core registry behind Blok.readyState() and Blok.subscribeReady(), so they cannot drift. It answers the question a comments list or a form actually has: are MY editors ready? Scope it with the ref you already hold on the container, so an unrelated editor elsewhere on the page cannot hold your gate closed. It is a live signal, not a one-shot latch: an editor mounted later re-closes the gate, and with settleOn: 'rendered' so does every re-render from a changed data prop. A scope holding no editors is ready, so the empty-list case needs no special-casing. It starts false and takes its first real reading once the scope element is attached (React: the mount effect; Vue: onMounted; Angular: afterNextRender), and a scope you asked for that has not resolved yet reports false rather than silently falling back to the whole page — over-waiting is safe, under-waiting is a bug.
options.settleOn'ready' | 'rendered''ready'Live readiness of the Blok editors inside a DOM subtree, as a boolean you can render from — the useBlokReady(options) hook in @bloklabs/react, the useBlokReady(options) composable in @bloklabs/vue (returns a ref), and injectBlokReady(options) in @bloklabs/angular (returns a signal). All three wrap the same core registry behind Blok.readyState() and Blok.subscribeReady(), so they cannot drift. It answers the question a comments list or a form actually has: are MY editors ready? Scope it with the ref you already hold on the container, so an unrelated editor elsewhere on the page cannot hold your gate closed. It is a live signal, not a one-shot latch: an editor mounted later re-closes the gate, and with settleOn: 'rendered' so does every re-render from a changed data prop. A scope holding no editors is ready, so the empty-list case needs no special-casing. It starts false and takes its first real reading once the scope element is attached (React: the mount effect; Vue: onMounted; Angular: afterNextRender), and a scope you asked for that has not resolved yet reports false rather than silently falling back to the whole page — over-waiting is safe, under-waiting is a bug.
TypeScript
const ready = useBlokReady({ within: listRef, settleOn: 'rendered' });
TypeScript
import { useRef } from 'react';
import { BlokEditor, useBlokReady } from '@bloklabs/react';

export function Comments({ comments }) {
  const listRef = useRef<HTMLDivElement>(null);

  // True once every editor inside listRef has its content in the DOM.
  // Re-arms whenever a comment's data changes and it re-renders.
  const ready = useBlokReady({ within: listRef, settleOn: 'rendered' });

  return (
    <>
      <div ref={listRef}>
        {comments.map((c) => (
          <BlokEditor key={c.id} data={c.body} readOnly />
        ))}
      </div>
      {!ready && <Skeleton />}
      <Composer autoFocus={ready} />
    </>
  );
}

// Vue — a ref:
// const list = ref<HTMLElement | null>(null);
// const ready = useBlokReady({ within: list, settleOn: 'rendered' });

// Angular — a signal, from an injection context:
// @ViewChild('list', { static: true }) listRef!: ElementRef<HTMLElement>;
// readonly ready = injectBlokReady({
//   within: () => this.listRef?.nativeElement ?? null,
//   settleOn: 'rendered',
// });