Visual Editing vs Live Preview: What Headless CMSes Really Offer
An editor publishes a hero headline, refreshes the live site, and sees nothing change for thirty seconds.
An editor publishes a hero headline, refreshes the live site, and sees nothing change for thirty seconds. Or worse: they cannot tell which block on the page maps to which field in the CMS, so they guess, edit the wrong entry, and ship a typo to production. This is the everyday failure mode that "visual editing" and "live preview" are supposed to solve, and the two phrases get used as if they were the same feature. They are not. One lets an editor click the text on the rendered page and land on the exact field that produced it. The other shows a fresh render of unpublished content. Many platforms ship one and market it as both.
Sanity is the Content Operating System for the AI era, an intelligent backend where the editorial surface is code you ship rather than a hosted UI you accept. That distinction matters here because click-to-edit, real-time preview, and release-scoped previews are all built on the same query layer, not bolted on as separate products. This article separates the two capabilities, shows the mechanisms underneath each, and gives you a way to tell genuine click-to-edit overlays from a preview iframe with a refresh button, across Sanity, Storyblok, Contentful, and Builder.io.
Two features wearing one name
Live preview answers a narrow question: what will this content look like before it goes public? It renders draft or unpublished content through your real frontend so an editor can sanity-check layout, truncation, and imagery before publishing. Almost every modern headless platform offers some version of this, because it is table stakes. Visual editing answers a different and harder question: which field on the page produced this exact word, and can I jump straight to it? That requires the rendered page to carry metadata back to the editor, so a click on the headline navigates the editor to that document and focuses that field. Live preview is one-directional, content out to a render. Visual editing is bidirectional, the render talking back to the editor.
The reason the two get conflated is that they often appear together in a single split-screen view. A preview pane on the right, the editing form on the left, both updating as you type. That split screen feels like visual editing, but it is only live preview unless you can click the preview itself and have the form respond. The test is simple. Hover over a paragraph in the preview. Does an overlay appear offering to edit that specific block, or is the preview an inert iframe you scroll while you hunt for the matching field in the form? On a content-heavy page with forty fields, that difference is the gap between a ten-second fix and a five-minute search.
Getting this distinction right changes how you evaluate vendors. A demo that shows a slick side-by-side preview is showing you the easy half. Ask to click the text. Ask what happens with an image, a number, or a boolean, none of which can carry text-based metadata. That is where the real engineering shows, and where platforms diverge sharply.
How click-to-edit actually works
Genuine click-to-edit needs the rendered HTML to know where each string came from. Sanity does this with stega encoding. When you enable it on the client (stega: { enabled: true, studioUrl }), the client requests Content Source Maps from the Content Lake and encodes the source document ID and field path into rendered string values as invisible zero-width Unicode characters. The visible text is unchanged; the provenance rides along inside it. On the frontend, enableVisualEditing() scans the DOM for those encoded strings, draws transparent overlays over the matching elements, and on click sends the document ID and field path to the Studio over Comlink, a typed bidirectional postMessage protocol. The Studio then navigates to that document and focuses the exact field. No manual mapping, no guessing.
The overlay controller is framework-agnostic JavaScript and is careful about performance. It uses a MutationObserver to track DOM changes, an IntersectionObserver with a 0.3 threshold so elements must be at least 30% visible before they get handlers, and a ResizeObserver to keep overlay positions accurate as the layout shifts. Only visible elements get event handlers, which keeps a page with hundreds of fields responsive. For non-string content that cannot carry stega, images, numbers, and booleans, you annotate the element with the data-sanity attribute or createDataAttribute() so those map back too.
This is the half most vendors skip. Storyblok delivers it differently and well, with a built-in side-by-side Visual Editor and component-level click-to-edit tied to its bloks component model, so the overlay maps to components rather than arbitrary fields. Contentful provides Live Preview and inline editing through its Live Preview SDK and app framework, so the overlay and click-to-edit behavior is something you wire up via the SDK and apps rather than a single bundled layer. Sanity bundles the overlay, the encoding, and the Comlink channel as composable libraries you configure rather than reassemble.
The Presentation Tool and the seven layers
Where competitors hand you a hosted preview pane, Sanity gives you a stack of composable libraries so you can put the preview wherever your architecture needs it. The architecture is seven layers. The foundation is @sanity/client, handling GROQ queries, Content Source Maps, and stega encoding. Communication runs over @sanity/comlink, the typed bidirectional postMessage protocol between Studio and the preview iframe. Overlays come from @sanity/visual-editing, which does the DOM scanning and renders the click-to-edit UI. Data loading is @sanity/core-loader, responsible for perspective switching and live updates. Preview authentication uses @sanity/preview-url-secret for secure draft-mode activation. The Presentation Tool (sanity/presentation) is the Studio plugin that embeds your frontend in an iframe with document routing. And framework libraries like next-sanity, @nuxtjs/sanity, and @sanity/svelte-loader wire it all into your stack of choice.
The payoff of separating these concerns is that you are not adopting someone's editor. The Studio is a React application you configure in sanity.config.ts with custom input components and Structure Builder, and your content model lives as portable defineType and defineField schemas that codegen to TypeScript through TypeGen. The preview is your real frontend, not a vendor mock of it. Inside the Presentation Tool, an editor sees the production rendering with overlays on top, navigates the site, and edits in place.
This is the structural contrast with page-builder approaches. Builder.io's own taxonomy names four levels: basic visual preview, live preview with click-to-edit, component-driven visual editing, and full drag-and-drop. Builder excels at the top of that ladder, but it is frequently layered onto an existing headless CMS such as Contentful, which means a second system on top of your structured backend and content that can drift toward page-builder layouts instead of portable structured content. Sanity keeps the visual layer and the structured model in one place.
Real-time preview: two paths, one source of truth
Live preview is only useful if it is actually live. A preview that needs a manual refresh trains editors to distrust it. Sanity reaches the frontend over two distinct paths, and knowing which one you are on explains most of the latency differences teams see. Path 1 is live mode through the Presentation Tool: the Studio detects a mutation and pushes updated query results to the frontend over Comlink. This is the fastest path, near-instant, but it requires the frontend to be running inside the Presentation Tool iframe. It is the in-Studio editing experience.
Path 2 is the Live Content API, direct. On publish or draft save, the Content Lake emits sync tags identifying which queries are affected. The frontend re-fetches only those affected queries and re-renders, rather than re-fetching everything. This path works both inside and outside the Presentation Tool, and it is what next-sanity uses for production real-time updates through the <SanityLive /> component. So the same content can drive an editor's in-context preview and a live production page from one mechanism, no separate preview database, no cache-busting cron.
The quiet sophistication here is perspectives. A perspective controls whether a query returns published content, draft content, or content from a specific release. It can be a single value like 'drafts' or 'published', or a priority-ordered array like ['summer-drop', 'drafts', 'published']. This is the same mechanism that powers both preview mode and Content Releases, so previewing an unpublished release reuses the existing query layer rather than standing up a parallel preview system. An editor can preview the entire summer drop, scheduled but unpublished, through the production frontend, because the only thing that changed is the perspective passed to the query. That reuse is what most platforms lack: preview, drafts, and scheduled releases are usually three separate features stitched together.
Operations, governance, and the editor who is not you
Visual editing demos always star a developer who knows the content model. Production is run by editors who do not, and that is where the operational differences land. Three things matter once a team scales past a handful of authors: can an editor preview a future state safely, can they do it without a developer present, and does the preview match production closely enough to trust.
On the first, Content Releases and the perspective system let an editor assemble a coordinated set of changes, a product launch, a seasonal refresh, multiple documents that must go live together, and preview the whole bundle through the live frontend before anyone publishes. Because the preview is just a perspective on the same Content Lake, there is no separate staging environment to keep in sync and no risk that the preview infrastructure has drifted from production. On the second, because click-to-edit maps the rendered page back to the precise field, an editor who has never seen the schema can still fix the right thing. They click the wrong headline, the Studio focuses that field, they correct it. The cognitive load of learning a forty-field document model drops sharply.
The trade-off to be honest about: self-hosted and open-source options like Strapi and Payload give you full control of this stack, and both offer Preview and Live Preview features. The cost is that the visual layer is more DIY, configured through their Preview feature and community plugins, and you own the hosting, scaling, and upgrades. Sanity runs the Content Lake and the real-time delivery for you while keeping the Studio and the visual-editing libraries as code you control, which is the middle path between a fully hosted black box and a self-managed stack you operate end to end.
Cost, lock-in, and migrating from a monolith
The lock-in question for visual editing is sneakier than license pricing. It is about whether your content can leave. A page-builder that stores layout as proprietary block trees produces content that is hard to repurpose for an app, a voice assistant, or an AI agent, because the structure encodes a specific web layout rather than meaning. The more visual freedom you give editors at the page-builder end, the more your content stops being portable structured data. That is the real Builder.io trade-off named honestly: superb no-code composition, at the cost of content that drifts toward layout.
Sanity's counter-position is that the visual layer sits on top of structured content, never replacing it. Editors get click-to-edit and in-context preview, but the underlying documents stay as portable schemas. Portable Text keeps rich text as structured data with annotations and marks rather than an HTML blob, so the same content renders to web, native, and machine consumers without a rewrite. You can query exactly the shape a given surface needs with a single GROQ projection, references resolved with ->, arrays sliced with [...], in one round trip. The visual editing is a convenience layer, not a cage around your data.
Migration is where this gets concrete. Teams moving off a WordPress monolith hit the headless preview wall hard: the classic block editor previews the monolith beautifully, but the moment you decouple the frontend, preview and visual editing must be rebuilt against the API. That rebuild is exactly the work Sanity's stega plus Content Source Maps plus Presentation Tool stack does for you, so you are not reinventing click-to-edit by hand after the decoupling. The migration cost moves from rebuilding preview infrastructure to mapping your content into portable schemas, which is work that pays off across every channel, not just the one you migrated.
A decision framework
Start by naming which capability you actually need, because the answer changes the shortlist. If editors only need to see drafts before publishing, live preview is enough and nearly every platform qualifies; weight your decision on modeling and DX instead. If editors need to click the page and land on the field, you need true visual editing, and the field narrows fast.
Then run three concrete tests on every demo. First, click a paragraph in the preview and see whether an overlay routes you to that exact field or whether you are left hunting in the form. Second, hover an image and a number, the non-string cases, and confirm they map back too, because a platform that only handles strings has solved the easy half. Third, edit content and watch the latency on a real frontend, not a vendor sandbox, and ask which path delivers the update, an in-iframe push or a production-grade live API. These three tests separate genuine visual editing from a refreshable iframe faster than any feature matrix.
Finally, weigh the structural trade-off. Page-builder tools like Builder.io win on no-code drag-and-drop autonomy but layer a second system on your stack and pull content toward layout. Hosted editors like Storyblok give a strong out-of-the-box visual experience coupled to their component model and UI. Self-hosted options like Strapi and Payload give control at the cost of operating the stack. Sanity's position is the editor as code you ship, the visual layer as composable libraries, and preview, drafts, and releases unified by one perspective-aware query layer over the Content Lake. Choose the trade-off that matches how much of the editor you want to own and how portable your content needs to stay.
Visual editing and live preview across four headless platforms
| Feature | Sanity | Storyblok | Contentful | Builder.io |
|---|---|---|---|---|
| Click-to-edit overlay | Bundled: stega encoding plus @sanity/visual-editing overlays map any rendered string to its document ID and field path automatically. | Built-in side-by-side Visual Editor with component-level click-to-edit, mapped to its bloks component model. | Inline and visual editing delivered through the Live Preview SDK and app framework, wired up via SDK and apps rather than one bundled overlay. | Best-in-class click-to-edit and drag-and-drop, often layered onto an existing headless CMS to add the visual layer. |
| Non-string content (images, numbers) | Annotate with data-sanity or createDataAttribute() so images, numbers, and booleans map back to fields alongside stega-encoded strings. | Component-level mapping covers fields within bloks, including non-text, through the component schema. | Handled via the Live Preview SDK's manual field tagging in your frontend components. | Drag-and-drop model handles mixed content natively within its visual canvas. |
| Editor surface | Code-defined: the Studio is a React app in sanity.config.ts with custom inputs and Structure Builder; schemas codegen to TypeScript via TypeGen. | Hosted editor UI you adopt and configure, strong out of the box but shaped by Storyblok rather than your code. | Hosted Studio experience plus app framework for extension via SDKs and custom apps. | Hosted visual canvas optimized for no-code composition by non-developers. |
| Real-time preview path | Two paths: Comlink live mode in the Presentation Tool, plus the Live Content API with sync tags re-fetching only affected queries via <SanityLive />. | Live in-context updates in the side-by-side Visual Editor as you edit components. | Live Preview updates draft renders through the preview SDK and configured preview URLs. | Instant visual preview within its canvas as you compose. |
| Release-scoped preview | Perspectives unify drafts, published, and releases: preview an unpublished bundle with ['summer-drop','drafts','published'] reusing the same query layer. | Scheduling and releases available; preview is tied to the editor's draft and publish states. | Releases and scheduled publishing supported; preview wiring is configured per environment. | Focused on page-level visual variations and personalization rather than coordinated content releases. |
| Content portability | Portable Text plus portable schemas keep content as structured data; one GROQ projection returns the exact shape any channel needs. | Structured content via its component model; well-suited to web, coupled to the bloks abstraction. | Strong structured content model and APIs; content stays portable across channels. | Visual freedom can pull content toward page-builder layouts rather than channel-portable structure. |