Visual editor
Editors see your real website next to the form, update it live while they type, and click blocks to edit them.
- In Settings → Preview, add your site's preview URL (e.g.
https://www.example.comorhttp://localhost:4000). - Mark blocks: spread
editable(block)(React),v-editable(Vue) oruse:editable(Svelte) on each block's root element. - Use
useLiveEntry/liveEntryfor the page anduseLiveGlobals/liveGlobalsforgetGlobals(), so unsaved changes show up immediately. - Verify the editor ticket on your server and switch to draft content with a preview token:
import { verifyEditorTicket } from "@blokcms/client";
const editor = await verifyEditorTicket(request.url, process.env.BLOKCMS_PREVIEW_SECRET);
const client = editor
? blok.with({ token: PREVIEW_TOKEN, draft: !editor.release, release: editor.release ?? undefined })
: blok;
The preview secret (Settings → Preview) signs short-lived tickets, so a leaked preview link stops working after a few minutes. The page runs inside an iframe: allow the admin origin in your frame-ancestors.
Globals
Globals (site settings, an announcement bar, the footer) have no page of their own: the editor previews them on a page you pick, the start page first. When your page reads them through useLiveGlobals (or useLiveEntry for a single getGlobal), they update while you type; otherwise the preview reloads after each save. Load globals in draft mode in the editor, like the page:
const [page, site] = await Promise.all([
client.getEntryByPath(path, { include: ["*"] }),
client.getGlobals(), // every published global; drafts with a preview token
]);
Not using a framework? Load https://docs.blokcms.app/bridge/v1.js and call BlokBridge.initBridge({ onUpdate }).
Editing on the page
Clicking a block opens its fields, with the field you clicked focused. The selected block gets a toolbar: select its parent, move it, duplicate it, comment on it or delete it, and add a block before or after it with the + buttons. Drag its grip to move it, or drag a type from Add blocks onto the page; only places that take that type light up. Everything can be undone (⌘Z), also when someone else edits the same entry.
Two optional markers make this more precise. Without them, the editor finds the clicked field by its text.
<h1 {...editableField(block, "headline")}>{fields.headline}</h1> // the click focuses Headline
<div {...editableList(block, "items")}> // an empty list shows "Add card"
<Blocks blocks={fields.items} />
</div>
In Vue use v-bind="editableField(block, 'headline')"; in Svelte spread editableFieldAttrs(block, "headline") and editableListAttrs(block, "items"). Without a framework, set data-blok-field="<blockId>:<key>" and data-blok-list="<blockId>:<field>".
Clicks select blocks. To use accordions, tabs or forms in the preview, switch the toolbar to Interact, or hold Alt while clicking. Shortcuts work in the preview too: Esc selects the parent, Delete removes, ⌘D duplicates, Alt+↑/↓ moves, ⌘C/⌘V copy and paste blocks (also between entries), ⌘S saves.
Header and footer usually come from a global. Clicking them offers to edit that global on the same page. Report the roots of what you show by passing useLiveEntry/useLiveGlobals responses (they do this for you), or live: () => [{ id: entryId, root: content.id }] to initBridge.
Troubleshooting
- "The site isn't connected": check that the site runs at the preview URL, that the page starts the bridge (
useLiveEntryorinitBridge), and that a customadminOriginsincludes the admin's origin. - A blank or refused frame: the site sends
X-Frame-Optionsor aContent-Security-Policywithout the admin inframe-ancestors. Allowhttps://app.blokcms.appfor preview requests. - No toolbar on the selected block: the site runs an older
@blokcms/bridge. Update it; the hostedv1.jsis always current. - Clicking does nothing: the element isn't inside an element with
editable(block). Mark each block's root element.