Next.js

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:

  1. Draft Mode — Access unpublished content during preview sessions

  2. Real-time Updates — See content changes reflected immediately without page refresh

  3. Content Link — Click-to-edit overlays that connect frontend elements to their CMS fields

  4. 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.

Click-to-edit overlays
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

Side-by-side editing

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.

Content Link overlays
How it works
  1. Stega encoding — When fetching draft content, pass the contentLink and baseEditingUrl options to embed invisible metadata into text fields

  2. Detection — The <ContentLink /> component scans your page for this encoded content

  3. Overlays — Interactive overlays appear when editors hover over editable content

  4. 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.

Side-by-side editing in DatoCMS
How it works

The plugin communicates with your frontend through two API endpoints:

  1. Preview Links API — Receives record info from DatoCMS and returns preview URLs (see src/app/api/preview-links/route.tsx for the full implementation):

app/api/preview-links/route.js
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}` }]
});
}
  1. 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:

  1. Install the Web Previews plugin from the marketplace

  2. Configure a frontend with:

    • Preview Links API endpoint: https://yoursite.com/api/preview-links?token=your-secret

    • Enable Draft Mode route: https://yoursite.com/api/draft-mode/enable?token=your-secret

For full configuration details, see the Web Previews plugin documentation.

Content Security Policy

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:

next.config.js
headers: async () => [{
source: '/:path*',
headers: [{
key: 'Content-Security-Policy',
value: "frame-ancestors 'self' https://plugins-cdn.datocms.com"
}]
}]

Last updated: February 3rd, 2026