Hygraph
Docs

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

PropertyRequiredDescription
overlayEnabledOptionalSet 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.
<HygraphPreview
endpoint={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

HookDescription
usePreviewRefreshReturns a framework-aware refresh() helper. Falls back to window.location.reload() when no framework integration is detected.
usePreviewRemixSubscribes to save events and revalidates with the Remix revalidator when available.
usePreviewFieldUpdatesCallbacks for preview:field-updated and preview:update-failed when sync.fieldUpdate is enabled.
usePreviewConnectionReturns { isConnected, isReady, mode }.
usePreviewActionsReturns { refresh, destroy, getVersion, getMode } for manual control.
usePreviewDebugReturns 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 (
<HygraphPreviewNextjs
endpoint={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.

MethodDescription
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 = `
<h1
data-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:

  1. A container element with data-hygraph-entry-id and data-hygraph-field-api-id pointing to the component array field
  2. Direct children with data-hygraph-component-chain so 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 (
<div
key={block.id}
data-hygraph-component-chain={JSON.stringify(componentChain)}
>
<ContentBlock
block={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:

  1. Finds the container via data-hygraph-entry-id and data-hygraph-field-api-id
  2. Reads data-hygraph-component-chain from each direct child to map component IDs to DOM elements
  3. Reorders existing DOM elements to match the new array order
  4. Removes elements for deletions
  5. 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-id and data-hygraph-field-api-id matching the component array field.
  • Confirm each direct child has data-hygraph-component-chain with the component instance ID.
  • Enable debug={true} (or debug: true) to see [ContentUpdater] COMPONENT_ARRAY logs.
  • New unsaved components appear only after saving and refreshing. Reordering and deletion of existing components work immediately when sync.fieldUpdate is enabled.

#DOM events

The SDK dispatches these events on document. Listen with addEventListener or usePreviewEvent.

Eventevent.detailWhen 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.

HelperDescription
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