Visual Editing
Visual Editing represents the ultimate content management experience — the "holy grail" for content editors. Instead of navigating through forms and fields in a CMS interface, editors can see their content exactly as it appears on the live site, click directly on any element to edit it, and watch changes appear instantly.
This seamless experience is achieved by combining several techniques that work together:
Draft Mode — Access unpublished content during preview sessions
Real-time Updates — See content changes reflected immediately without page refresh
Content Link — Click-to-edit overlays that connect frontend elements to their CMS fields
Web Previews Plugin — The DatoCMS plugin that orchestrates the editing experience
This guide focuses on Content Link and Web Previews — the final pieces that transform a preview into a true visual editing environment.
Two levels of integration
Visual Editing can be set up incrementally:
Level 1: Content Link (standalone)
With just Content Link configured, editors browsing your website in draft mode can click on any content element to edit it. Clicking opens DatoCMS in a new browser tab, navigating directly to the field that controls that content.
This works entirely on your website — no DatoCMS plugin required. It's a great starting point that already provides significant value to editors.
Level 2: Web Previews Plugin (side-by-side)
Adding the Web Previews plugin takes it further: editors can now view the website and DatoCMS interface side-by-side within DatoCMS itself. When they click on content, the edit panel opens instantly in the same view — no tab switching required.
The plugin also enables:
Preview links in the DatoCMS sidebar
Bidirectional navigation (browse the preview, and DatoCMS follows along)
Full-screen Visual Editing mode
Content Link: Click-to-edit overlays
Content Link enables the "click-to-edit" functionality by embedding invisible metadata (called "stega encoding") into your content. When editors hover over content in draft mode, visual overlays appear indicating which elements are editable.
This works entirely on your website — editors simply browse the site in draft mode, and clicking any editable element opens DatoCMS in a new tab. No plugin installation required.
How it works
Stega encoding — When fetching draft content, pass the
contentLinkandbaseEditingUrloptions to embed invisible metadata into text fieldsDetection — The
<ContentLink />component scans your page for this encoded contentOverlays — Interactive overlays appear when editors hover over editable content
Deep linking — Clicking an element opens DatoCMS at the exact field that controls that content
Setting up Content Link
The setup involves two parts:
Enable stega encoding when fetching draft content by passing the contentLink and baseEditingUrl options (see src/lib/datocms/executeQuery.ts for the full implementation):
performRequest(query, { includeDrafts: true, contentLink: 'v1', baseEditingUrl: 'https://your-project.admin.datocms.com',});Add the <ContentLink /> component to your root layout, rendered only in draft mode (see src/components/ContentLink and src/app/layout.tsx for implementation details):
{isDraftModeEnabled && <ContentLink />}For more advanced use cases — like building a custom toolbar to toggle edit mode, or programmatically triggering the "flash all editable elements" animation — use the useContentLink hook:
const { enableClickToEdit, disableClickToEdit, flashAll } = useContentLink();For component props and keyboard shortcuts, see the react-datocms ContentLink documentation.
Working with Structured Text
Structured Text fields require two rules for Visual Editing to work correctly.
Rule 1: Always wrap the Structured Text component in a group. This makes the entire structured text area clickable, instead of just the tiny stega-encoded span:
<div data-datocms-content-link-group> <StructuredText data={content.body} /></div>Rule 2: Wrap embedded blocks, inline records, and inline blocks in a boundary. These elements have their own edit URL (pointing to the block/record). Without a boundary, clicking them would bubble up to the parent group and open the structured text field editor instead. Note that renderLinkToRecord does not need a boundary — record links are just <a> tags wrapping text that belongs to the surrounding structured text, so there's no URL collision.
<div data-datocms-content-link-group> <StructuredText data={page.content} renderBlock={({ record }) => ( <div data-datocms-content-link-boundary> <BlockComponent block={record} /> </div> )} renderInlineRecord={({ record }) => ( <span data-datocms-content-link-boundary> <InlineRecordComponent record={record} /> </span> )} renderLinkToRecord={({ record, children, transformedMeta }) => ( <a {...transformedMeta} href={`/resources/${record.slug}`}> {children} </a> )} renderInlineBlock={({ record }) => ( <span data-datocms-content-link-boundary> <InlineBlockComponent record={record} /> </span> )} /></div>See the ContentLink Structured Text documentation for details.
Web Previews Plugin (optional enhancement)
The
Web Previews plugin enhances the editing experience by embedding your website preview directly inside DatoCMS. Instead of switching between browser tabs, editors get a side-by-side view where clicking on content instantly opens the edit panel.
When Content Link detects it's running inside the Web Previews plugin iframe, it automatically switches from opening new tabs to communicating with the plugin — no code changes required.
How it works
The plugin communicates with your frontend through two API endpoints:
Preview Links API — Receives record info from DatoCMS and returns preview URLs (see
src/app/api/preview-links/route.tsxfor the full implementation):
export async function POST(request) { const { item, locale } = await request.json(); const url = await recordToWebsiteRoute(item, locale);
return NextResponse.json({ previewLinks: [{ label: 'Draft', url: `/api/draft-mode/enable?redirect=${url}` }] });}Enable Draft Mode route — Activates draft mode and redirects to the preview. This is the same route covered in the Draft Mode guide.
Configuring the plugin
In your DatoCMS project:
Install the Web Previews plugin from the marketplace
Configure a frontend with:
Preview Links API endpoint:
https://yoursite.com/api/preview-links?token=your-secretEnable Draft Mode route:
https://yoursite.com/api/draft-mode/enable?token=your-secret
For full configuration details, see the Web Previews plugin documentation.
If your website implements a Content Security Policy with a frame-ancestors directive, you need to allow the DatoCMS plugin to embed your site. In Next.js, this is typically configured via the headers option in next.config.js:
headers: async () => [{ source: '/:path*', headers: [{ key: 'Content-Security-Policy', value: "frame-ancestors 'self' https://plugins-cdn.datocms.com" }]}]