Troubleshooting Visual Editing
Overlay and highlighting problems
Text breaks out of its container, or the container is too wide
If the text on the page breaks out of its container, or the container is much wider than normal, split the encoded text out from the original text.
Note
This is not due to the encoded characters themselves. This problem occurs only when the element also uses negative letter-spacing in its CSS, or sits inside a <Balancer> component from react-wrap-balancer.
Identify where the problematic element is rendered in your code, for example:
export function MyComponent({ text }: { text: string }) {
return <h1>{text}</h1>;
}Rewrite using @vercel/stega to avoid any styling issues:
import { vercelStegaSplit } from "@vercel/stega";
export function MyComponent({ text }: { text: string }) {
const { cleaned, encoded } = vercelStegaSplit(text);
return (
<h1>
{cleaned}
<span style={{ display: "none" }}>{encoded}</span>
</h1>);
}If you need this more than once, extract the logic into a reusable component:
import { vercelStegaSplit } from "@vercel/stega";
export default function Clean({ value }: { value: string }) {
const { cleaned, encoded } = vercelStegaSplit(value);
return encoded ? (
<>
{cleaned}
<span style={{ display: "none" }}>{encoded}</span>
</>) : (
cleaned
);
}
export function MyComponent({ text }: { text: string }) {
return (
<h1>
<Clean value={text} />
</h1>);
}Overlay displays over the wrong element
If the wrong element is highlighted when you hover, add an attribute to a containing element.
For example, if this component highlights the <h1> and you want it to highlight the <section> element:
<section>
<h1>{dynamicTitle}</h1>
<div>Hardcoded Tagline</div>
</section>Add a data attribute to highlight the correct item:
- For Visual Editing with
@sanity/visual-editing, adddata-sanity-edit-target. - For Vercel Visual Editing (Vercel's Edit Mode), add
data-vercel-edit-target.
<section data-sanity-edit-target>
<h1>{dynamicTitle}</h1>
<div>Hardcoded Tagline</div>
</section>Overlay can’t resolve a field for an element
@sanity/visual-editing logs [@sanity/visual-editing] No field could be resolved at path: "FIELD_PATH" to the browser console when the path encoded in an element doesn’t match any field in the schema your Studio reported. Visual Editing catches the error and continues without field information for that element.
The encoded path and the schema have drifted apart. Check that:
- The schema deployed to your Studio still contains the field the path names.
- The element renders a value from the document the path belongs to, and not a stega-carrying value copied in from another field.
Stega encoding problems
As of @sanity/visual-editing 5.5.0 and Sanity Studio 6.6.0, two common sources of stega contamination are handled automatically. First, clipboard copies are cleaned. When Visual Editing is active, <VisualEditing /> strips stega from clipboard data as you copy text from the preview page. Pasting into external tools such as Notion, Slack, or spreadsheets no longer produces invisible characters. You can opt out of this behavior with the keepStegaOnCopy prop if needed.
Second, Sanity Studio cleans field pastes automatically: pasting stega-contaminated text into any Sanity Studio primitive field (string, text, email, url, slug, tags, number, and arrays of primitives, plus any custom input that spreads elementProps onto a native input or textarea) now strips stega before storing the value. Portable Text already handled this; as of 6.6.0, all primitive fields do as well.
There are weird characters in your DOM
These are most likely stega-encoded strings. They are a subset of HTML entities that, when rendered, produce invisible output. This is the string value of “Oxford Shoes” when it contains a stega-encoded Content Source Map:
Oxford Shoes​​​​‌‍​‍​‍‌‍‌​‍‌‍‍‌‌‍‌‌‍‍‌‌‍‍​‍​‍​‍‍​‍​‍‌​‌‍​‌‌‍‍‌‍‍‌‌‌​‌‍‌​‍‍‌‍‍‌‌‍​‍​‍​‍​​‍​‍‌‍‍​‌​‍‌‍‌‌‌‍‌‍​‍​‍​‍‍​‍​‍‌‍‍​‌‌​‌‌​‌​​‌​​‍‍​‍​‍‌‌‍‌‍‍‌‌​‌‌‌‌‍​‌‌‍​​‍‌‌‍‌‌‌‍‌​‌‍‍‌‌‌​‌‍‍‌‌‍‍‌‍‌​‍‌‌​‌‌​‌‌‌‌‍‌​‌‍‍‌‌‍​‍‍‌​‌‍​‌‌‍‍‌‍‍‌‌‌​‌‍‌​‍‍‌‍​‍‌‌‌‌‍‍‌‌‍​‌‍‌​​‍‌​‍‌‍‌‌‌‍‌‌‍‍‌‌‍​​‍‌‍‍‌‌‍‍‌‌​‌‍‌‌‌‍‍‌‌​​‍‌‍‌‌‌‍‌​‌‍‍‌‌‌​​‍‌‍‌‌‍‌‍‌​‌‍‌‌​‌‌​​‌​‍‌‍‌‌‌​‌‍‌‌‌‍‍‌‌​‌‍​‌‌‌​‌‍‍‌‌‍‌‍‍​‍‌‍‍‌‌‍‌​​‌‌‍​‍​‍‌​‌‌​‌‍‌‍​‌‍‌‍​‌‌‍​‍​‍‌‌‍​‌​‌‌‍​‍​‌​‍‌​‌​‌‍‌‌​‌‌​‌‍​‍‌‌‍​‌​​‌​‌‍‌‍‌‍​‍‌‌‍​‍‌‍​‌‌‍‌​​‍​‌‍‌‌‌‍‌‌​​​‍‌​​‍‌‍‌‌​​​‌​​‍‌‌​‌‍‌‌​​‌‍‌‌​‌‌​‌‍‍​‌‍‌‍‌‌​‍‌​​‌‍​‌‌‌​‌‍‍​​‌‌‌​‌‍‍‌‌‌​‌‍​‌‍‌‌​‍‌‌​‌‍‌‍‌‍​​‌‌​​‌​‍‌‍‌‌‌​‌‍‌‌‌‍‍‌‌​‌‍​‌‌‌​‌‍‍‌‌‍‌‍‍​‌‍​‍‌‍​‌‌​‌‍‌‌‌‌‌‌‌​‍‌‍​​‌‌‍‍​‌‌​‌‌​‌​​‌​​‍‌‌​​‌​​‌​‍‌‌​​‍‌​‌‍​‍‌‌​​‍‌​‌‍‌‌‍‌‍‍‌‌​‌‌‌‌‍​‌‌‍​​‍‌‌‍‌‌‌‍‌​‌‍‍‌‌‌​‌‍‍‌‌‍‍‌‍‌​‍‌‌​‌‌​‌‌‌‌‍‌​‌‍‍‌‌‍​‍‍‌​‌‍​‌‌‍‍‌‍‍‌‌‌​‌‍‌​‍‍‌‍​‍‌‌‌‌‍‍‌‌‍​‌‍‌​​‍‌‍‌‍‍‌‌‍‌​​‌‌‍​‍​‍‌​‌‌​‌‍‌‍​‌‍‌‍​‌‌‍​‍​‍‌‌‍​‌​‌‌‍​‍​‌​‍‌​‌​‌‍‌‌​‌‌​‌‍​‍‌‌‍​‌​​‌​‌‍‌‍‌‍​‍‌‌‍​‍‌‍​‌‌‍‌​​‍​‌‍‌‌‌‍‌‌​​​‍‌​​‍‌‍‌‌​​​‌​​‍‌‍‌‌​‌‍‌‌​​‌‍‌‌​‌‌​‌‍‍​‌‍‌‍‌‌​‍‌‍‌​​‌‍​‌‌‌​‌‍‍​​‌‌‌​‌‍‍‌‌‌​‌‍​‌‍‌‌​‍‌‍‌‌‌‍‌​‍‌‍‍‌​‌​​‌‍​‌‌‍​‌‍‌‌​‌‌​‍‌‍‌‌‌‍‌‌‍‍‌‌‍​​‍‌‍‌‌​‌‍‌‍‌‍​​‌‌​​‌​‍‌‍‌‌‌​‌‍‌‌‌‍‍‌‌​‌‍​‌‌‌​‌‍‍‌‌‍‌‍‍​‍‌‍‌‍‍‌‌​‌​‌​‌​‍‌‍​‌‌‍‌‍‌‌​​‌​‍​‍‌‌
When rendered in an HTML document, this string still displays as “Oxford Shoes.” You can use the HTML entity decoder to test the string by pasting the encoded value into the “Encoded” input field.
Comparing field values doesn’t work in preview mode
Your application likely evaluates values from the Content Lake to perform specific logic. If these values contain invisible encoded metadata, they may no longer work.
For example, imagine a function that determines that a Sanity document's market value is the same as the current market:
function showDocument(document: SanityDocument, currentMarket: string) {
return document.market === currentMarket
}Without stega enabled, this function works as expected. However, if document.market contains encoded metadata, this comparison fails.
If document.market is never shown on the page and does not benefit from Visual Editing, filter it out of the stega encoding process. You can pass a filtering function to stega.filter when configuring the client.
Alternatively, clean the value before comparing it:
import {stegaClean} from "@sanity/client/stega"
import type {SanityDocument} from "@sanity/client"
function showDocument(document: SanityDocument, currentMarket: string) {
return stegaClean(document.market) === currentMarket
}If you need this more than once, extract it into a helper function.
Studio 6.6.0 and later strips stega when content is pasted into studio fields, so contamination in stored data is handled for you. stegaClean() is still required in your frontend code for string comparisons and URL construction.
Stega appears outside visible text
Stega in rendered visible text is intentional — it powers click-to-edit. Stega anywhere else always causes bugs and must be avoided:
- HTML element attributes such as
class,id,href,src,style, anddata-*. - Inside
<head>— page title,meta[content], and JSON-LD. <script>and<style>text content.<textarea>form values.- The page URL.
The onSuspiciousStega callback on <VisualEditing /> is an opt-in tool to detect these cases during development. It receives an array of reports, each with a report.kind property indicating where the stega was found:
<VisualEditing
onSuspiciousStega={(reports) => {
for (const report of reports) {
console.warn(`Stega found in ${report.kind}`, report)
}
}}
/>Performance cost
The onSuspiciousStega callback runs a full DOM audit using TreeWalker and MutationObserver and has a performance cost. It is most useful during development and debugging.
Possible report.kind values are: attribute, head, script, style, form-value, and url.
Version mismatch warnings
@sanity/visual-editing asks the Presentation tool in your Studio which features it supports. When your Studio is older than the frontend package expects, the request fails, Visual Editing skips the affected feature, and it logs a warning to the browser console. Update the sanity package in your Studio to clear these warnings.
@sanity/visual-editing logs [@sanity/visual-editing] Package version mismatch detected: Please update your Sanity studio to prevent potential compatibility issues. when the feature negotiation request fails. Optimistic updates stay disabled.
@sanity/visual-editing logs [@sanity/visual-editing]: Failed to fetch shared state. Check your version of `sanity` is up-to-date when the shared state request fails. The underlying reason is logged separately with console.debug, so turn on verbose console output to see it.
Embedded studio problems
When using Visual Editing with embedded studios (studios that render on a route in your frontend app), do not include the Visual Editing or SanityLive components in the root or layout component for your studio.
Create dedicated content and studio layouts so you can keep your studio separate from the Visual Editing components.