Was this page helpful?
Overlays are a core part of Sanity's Visual Editing that enables interactive editing experiences directly in your front end. They range from simple click-to-edit functionality to advanced drag-and-drop page building capabilities.
Overlays serve two main purposes in Visual Editing:
For overlays to function, they need to:
The simplest approach uses Stega encoding, which automatically adds invisible Content Source Maps to text content:
// Stega is usually enabled at the client level
const client = createClient({
// ...other config
stega: {
enabled: true
}
})Stega inserts hidden characters into your content, and can cause problems when comparing strings. While this only displays when previewing content, it can cause unexpected behavior. You can prevent this by cleaning any values before comparison with the stegaClean utility.
For non-text content or custom interactions, use data attributes:
import { createDataAttribute } from "@sanity/visual-editing"
function Section({ documentId, documentType, sections }) {
const attr = createDataAttribute({
id: documentId,
type: documentType,
path: 'sections'
})
return (
<div data-sanity={attr().toString()}>
{sections.map(section => (
<div
key={section._key}
data-sanity={attr(`sections[_key=="${section._key}"]`).toString()}
>
{section.content}
</div>
))}
</div>
)
}When using framework-specific loaders, you get a pre-configured encoding helper. Here is how it can look with the React loader:
export default function Page() {
const { data, encodeDataAttribute } = useQuery(query)
return (
<div data-sanity={encodeDataAttribute(['sections'])}>
{/* Your content */}
</div>
)
}Use the data-sanity-edit-group attribute to group multiple overlays onto a single visual element:
import { createDataAttribute } from "@sanity/visual-editing"
function Section({
documentId,
documentType,
backgroundColor,
borderColor
}) {
const attr = createDataAttribute({
id: documentId,
type: documentType,
})
return (
<div data-sanity-edit-group style={{backgroundColor, borderColor}}>
<div data-sanity={attr('backgroundColor').toString()} />
<div data-sanity={attr('borderColor').toString()} />
</div>
)
}Edit groups will ignore text-based nodes to keep the click-to-edit functionality on text content.
Overlays follow a progressive enhancement model based on your framework's capabilities:
createDataAttribute for non-text content and custom interactionsWhen using Vercel's Visual Editing:

// Stega is usually enabled at the client level
const client = createClient({
// ...other config
stega: {
enabled: true
}
})import { createDataAttribute } from "@sanity/visual-editing"
function Section({ documentId, documentType, sections }) {
const attr = createDataAttribute({
id: documentId,
type: documentType,
path: 'sections'
})
return (
<div data-sanity={attr().toString()}>
{sections.map(section => (
<div
key={section._key}
data-sanity={attr(`sections[_key=="${section._key}"]`).toString()}
>
{section.content}
</div>
))}
</div>
)
}export default function Page() {
const { data, encodeDataAttribute } = useQuery(query)
return (
<div data-sanity={encodeDataAttribute(['sections'])}>
{/* Your content */}
</div>
)
}import { createDataAttribute } from "@sanity/visual-editing"
function Section({
documentId,
documentType,
backgroundColor,
borderColor
}) {
const attr = createDataAttribute({
id: documentId,
type: documentType,
})
return (
<div data-sanity-edit-group style={{backgroundColor, borderColor}}>
<div data-sanity={attr('backgroundColor').toString()} />
<div data-sanity={attr('borderColor').toString()} />
</div>
)
}