#Click to Edit - Advanced API
This page covers advanced Preview SDK APIs beyond the standard Click to Edit setup. Use it when you need custom save handling, connection status, dynamic DOM updates, or lower-level control than PreviewWrapper provides.
For framework walkthroughs, see:
#Additional configuration
These options extend the configuration properties on the parent setup page.
| Property | Required | Description |
|---|---|---|
overlayEnabled | Optional | Set to false to disable hover overlays and Edit buttons while keeping the Studio connection active. Defaults to true. Available on both HygraphPreview and the core Preview constructor. |
<HygraphPreviewendpoint={process.env.NEXT_PUBLIC_HYGRAPH_ENDPOINT!}studioUrl={process.env.NEXT_PUBLIC_HYGRAPH_STUDIO_URL}overlayEnabled={false}onSave={() => router.refresh()}>{children}</HygraphPreview>
#React hooks
Import hooks from @hygraph/preview-sdk/react. They must run inside a tree wrapped by HygraphPreview.
#usePreview
Returns the Preview instance and connection state.
import { usePreview } from '@hygraph/preview-sdk/react';function PreviewStatus() {const { preview, isReady, isConnected } = usePreview();return (<p>Ready: {String(isReady)} · Connected: {String(isConnected)} · Mode:{' '}{preview?.getMode() ?? 'n/a'}</p>);}
#usePreviewSave
Subscribes to save events. Use this instead of (or in addition to) the onSave prop when save handling lives in a child component.
import { usePreviewSave } from '@hygraph/preview-sdk/react';import { useRouter } from 'next/navigation';function SaveListener() {const router = useRouter();usePreviewSave((entryId) => {console.log('Saved entry:', entryId);router.refresh();});return null;}
#usePreviewEvent
Subscribes to any DOM event the SDK dispatches on document.
import { usePreviewEvent } from '@hygraph/preview-sdk/react';function FieldClickListener() {usePreviewEvent('preview:field-click', (event) => {console.log('Field clicked:', event.detail);});return null;}
#Other React hooks
| Hook | Description |
|---|---|
usePreviewRefresh | Returns a framework-aware refresh() helper. Falls back to window.location.reload() when no framework integration is detected. |
usePreviewRemix | Subscribes to save events and revalidates with the Remix revalidator when available. |
usePreviewFieldUpdates | Callbacks for preview:field-updated and preview:update-failed when sync.fieldUpdate is enabled. |
usePreviewConnection | Returns { isConnected, isReady, mode }. |
usePreviewActions | Returns { refresh, destroy, getVersion, getMode } for manual control. |
usePreviewDebug | Returns registry stats and framework detection for development. |
#HygraphPreviewNextjs
Optional Next.js helper that wires refresh for you. Most apps use HygraphPreview with next/dynamic and onSave={() => router.refresh()} instead, as shown in the App Router guide.
import { HygraphPreviewNextjs } from '@hygraph/preview-sdk/react';import { useRouter } from 'next/navigation';export function PreviewWrapper({ children }: { children: React.ReactNode }) {const router = useRouter();return (<HygraphPreviewNextjsendpoint={process.env.NEXT_PUBLIC_HYGRAPH_ENDPOINT!}studioUrl={process.env.NEXT_PUBLIC_HYGRAPH_STUDIO_URL}refresh={router.refresh}>{children}</HygraphPreviewNextjs>);}
#Core Preview methods
When you initialize the SDK with new Preview() (vanilla JavaScript, Vue, Nuxt), use these methods on the instance.
| Method | Description |
|---|---|
subscribe('save', { callback }) | Listens for Studio save events. Returns an unsubscribe function. |
refresh() | Re-scans the DOM for data-hygraph-* attributes. Call after you inject or replace HTML without a full page reload. |
getMode() | Returns 'iframe' or 'standalone'. |
isConnected() | Returns whether the SDK is connected to Studio. |
getVersion() | Returns the SDK version string. |
configureOverlay(config) | Updates overlay styles at runtime. |
destroy() | Tears down listeners, overlays, and the Studio connection. Call on page unload in SPAs. |
getFrameworkIntegration() | Returns the SDK's framework integration helper used for framework-aware refresh. |
getFieldRegistryStats() | Returns counts and diagnostics for registered preview fields. Useful when debugging missing overlays. |
getFieldRegistryKeys() | Returns the registry keys for currently tracked fields. |
import { Preview } from '@hygraph/preview-sdk/core';const preview = new Preview({endpoint: process.env.HYGRAPH_ENDPOINT,studioUrl: process.env.HYGRAPH_STUDIO_URL,debug: true,});const unsubscribe = preview.subscribe('save', {callback: (entryId) => {console.log('Saved:', entryId);window.location.reload();},});window.addEventListener('beforeunload', () => {unsubscribe();preview.destroy();});
#Dynamic content without a full reload
If your app updates the DOM without navigating away, call refresh() after new marked-up HTML is in the page:
container.innerHTML = `<h1data-hygraph-entry-id="${entry.id}"data-hygraph-field-api-id="title">${entry.title}</h1>`;preview.refresh();
#Live preview for component arrays
When sync.fieldUpdate is enabled, the SDK can update component arrays (modular content) in the preview without a full page refresh:
- Reordering (drag-and-drop) — existing DOM elements move in place
- Deletion — removed components disappear immediately
- Addition — new unsaved components are skipped until you save and refresh the page
#Set up a component array container
The SDK needs:
- A container element with
data-hygraph-entry-idanddata-hygraph-field-api-idpointing to the component array field - Direct children with
data-hygraph-component-chainso the SDK can identify each component instance
import {createPreviewAttributes,createComponentChainLink,} from '@hygraph/preview-sdk/core';function ArticlePage({ article }) {return (<main><h1 {...createPreviewAttributes({ entryId: article.id, fieldApiId: 'title' })}>{article.title}</h1>{/* Container — the SDK targets this for reordering */}<div{...createPreviewAttributes({entryId: article.id,fieldApiId: 'content', // Must match the component array field API ID})}>{article.content.map((block) => {const componentChain = [createComponentChainLink('content', block.id)];return (<divkey={block.id}data-hygraph-component-chain={JSON.stringify(componentChain)}><ContentBlockblock={block}articleId={article.id}componentChain={componentChain}/></div>);})}</div></main>);}
#How it works
When Studio detects a structural change (reorder, add, or delete) in a component array, it sends a COMPONENT_ARRAY field update. The SDK then:
- Finds the container via
data-hygraph-entry-idanddata-hygraph-field-api-id - Reads
data-hygraph-component-chainfrom each direct child to map component IDs to DOM elements - Reorders existing DOM elements to match the new array order
- Removes elements for deletions
- Skips new unsaved components until save and refresh
For Rich Text fields rendered as HTML inside components, add data-hygraph-rich-text-format="html" so live field updates use the correct format.
#Troubleshooting component arrays
- Confirm the container has
data-hygraph-entry-idanddata-hygraph-field-api-idmatching the component array field. - Confirm each direct child has
data-hygraph-component-chainwith the component instance ID. - Enable
debug={true}(ordebug: true) to see[ContentUpdater] COMPONENT_ARRAYlogs. - New unsaved components appear only after saving and refreshing. Reordering and deletion of existing components work immediately when
sync.fieldUpdateis enabled.
#DOM events
The SDK dispatches these events on document. Listen with addEventListener or usePreviewEvent.
| Event | event.detail | When it fires |
|---|---|---|
preview:ready | { preview } | SDK finished initializing. |
preview:connected | { studioOrigin } | Connected to Studio in iframe mode. |
preview:disconnected | {} | Connection to Studio ended. |
preview:content-saved | { entryId, timestamp } | Studio reported a save. |
preview:field-click | { entryId, fieldApiId?, locale?, componentChain?, mode? } | Editor clicked an Edit overlay. |
preview:field-focus | { entryId, fieldApiId, locale? } | Studio requested field focus sync. |
preview:field-updated | { entryId, fieldApiId, newValue } | A live field update applied (sync.fieldUpdate: true). |
preview:update-failed | { entryId, fieldApiId, error } | A live field update failed. |
document.addEventListener('preview:ready', () => {console.log('Preview SDK ready');});document.addEventListener('preview:content-saved', (event) => {console.log('Content saved:', event.detail.entryId);});document.addEventListener('preview:field-click', (event) => {console.log('Field clicked:', event.detail);});
#Attribute helpers
These helpers from @hygraph/preview-sdk/core are used throughout the framework guides. The parent attribute reference covers the HTML attributes they produce.
| Helper | Description |
|---|---|
createPreviewAttributes({ entryId, fieldApiId?, locale?, componentChain? }) | Returns an object of data-hygraph-* attributes for JSX or v-bind. |
createComponentChainLink(fieldApiId, instanceId) | Builds one { fieldApiId, instanceId } link for nested components. |
withFieldPath(attributes, fieldPath) | Adds data-hygraph-field-path (for example, ingredients.0.quantity) for debugging nested fields. It does not change Studio focus behavior. |
import {createPreviewAttributes,createComponentChainLink,withFieldPath,} from '@hygraph/preview-sdk/core';const chain = [createComponentChainLink('ingredients', ingredient.id)];const quantityAttributes = withFieldPath(createPreviewAttributes({entryId: recipe.id,fieldApiId: 'quantity',componentChain: chain,}),`ingredients.${index}.quantity`);
createPreviewAttributes also accepts an optional locale, which sets data-hygraph-field-locale. Click to Edit still cannot jump to a non-default locale in Studio; see the known limitation.
#What's next
- Click to Edit setup: Install steps, attribute reference, Studio widget, and troubleshooting.
- Click to Edit - Vanilla JavaScript: UMD setup and server-rendered attribute examples.
- Preview SDK on GitHub: Source, changelog, and runnable examples.