Preview links allow accessing components and content via a shareable token without requiring an API key. This enables sharing draft work with stakeholders, testing components in isolation, and previewing unpublished content.
Authentication
Preview queries use a preview-token header instead of x-api-key:
headers: {
'preview-token': 'YOUR_PREVIEW_TOKEN',
'Content-Type': 'application/json'
}
For WebSocket subscriptions:
connectionParams: {
previewToken: 'YOUR_PREVIEW_TOKEN'
}
Preview Query
query GetPreview($token: String!, $version: Int) {
preview(token: $token, version: $version) {
... on ComponentPreview {
componentId
component {
id
name
title
compiled
schema
}
resolvedRef {
package
dependencies
}
}
... on ContentPreview {
contentId
content {
id
name
compiled
contentType {
name
schema
}
}
resolvedRef {
package
dependencies
}
}
}
}
Parameters
| Parameter | Type | Description |
|---|
token | String! | Preview link token |
version | Int | Optional: specific component version |
Component Preview
Fetch a component preview (defaults to latest draft):
query GetComponentPreview($token: String!) {
preview(token: $token) {
... on ComponentPreview {
componentId
component {
compiled
schema
}
resolvedRef {
package
dependencies
}
}
}
}
Response:
{
"data": {
"preview": {
"__typename": "ComponentPreview",
"componentId": "comp123",
"component": {
"compiled": "const body = (props) => { ... }",
"schema": "{\"type\":\"object\",\"properties\":{...}}"
},
"resolvedRef": {
"package": "draft:proj123:org456",
"dependencies": ["xyz789ghi012..."]
}
}
}
}
Specific Version
Fetch a specific published version of a component:
query GetComponentVersion($token: String!, $version: Int!) {
preview(token: $token, version: $version) {
... on ComponentPreview {
componentId
component {
compiled
}
resolvedRef {
package
dependencies
}
}
}
}
Variables:
{
"token": "abc123def456",
"version": 2
}
Content Preview
Fetch a content preview:
query GetContentPreview($token: String!) {
preview(token: $token) {
... on ContentPreview {
contentId
content {
id
name
compiled
tags
locale
contentType {
name
}
}
resolvedRef {
package
dependencies
}
}
}
}
Preview Subscription
Watch for real-time updates to previewed components or content:
subscription WatchPreview($token: String!) {
previewUpdated(token: $token) {
action
timestamp
preview {
... on ComponentPreview {
componentId
component {
compiled
}
resolvedRef {
package
dependencies
}
}
... on ContentPreview {
contentId
content {
compiled
}
resolvedRef {
package
dependencies
}
}
}
}
}
Subscriptions are only useful for draft components and unpublished content, as published versions are immutable.
Client Implementation
React Hook Example
import { useQuery, useSubscription } from '@apollo/client';
function usePreview(token, version) {
const { data, loading, error } = useQuery(GET_PREVIEW, {
variables: { token, version },
context: {
headers: { 'preview-token': token }
}
});
useSubscription(PREVIEW_UPDATED, {
variables: { token },
context: {
headers: { 'preview-token': token }
},
onData: ({ data }) => {
const update = data.data.previewUpdated;
// Handle update...
}
});
return { data: data?.preview, loading, error };
}
WebSocket Setup for Previews
import { createClient } from 'graphql-ws';
const previewClient = createClient({
url: 'wss://api.metabind.ai/graphql',
connectionParams: {
previewToken: 'YOUR_PREVIEW_TOKEN'
}
});
previewClient.subscribe({
query: PREVIEW_UPDATED_SUBSCRIPTION,
variables: { token: 'YOUR_PREVIEW_TOKEN' }
}, {
next: (data) => {
const update = data.data.previewUpdated;
if (update.action === 'delete') {
handlePreviewDeleted(update.preview);
return;
}
// Handle component or content update
if (update.preview.__typename === 'ComponentPreview') {
refreshComponentPreview(update.preview);
} else {
refreshContentPreview(update.preview);
}
},
error: (err) => console.error('Preview subscription error:', err)
});
Handling Large Payloads
When content exceeds the 128KB WebSocket limit:
- The
component or content field will be null
- The
resolvedRef is always included
- Fetch the full data separately using the component/content ID
if (!update.preview.component) {
// Component exceeded 128KB
const component = await fetchComponent(update.preview.componentId);
refreshComponentPreview(component, update.preview.resolvedRef);
} else {
refreshComponentPreview(update.preview.component, update.preview.resolvedRef);
}
Error Handling
Invalid Token
{
"errors": [
{
"message": "Invalid or expired preview token",
"extensions": {
"code": "INVALID_PREVIEW_TOKEN"
},
"path": ["preview"]
}
],
"data": { "preview": null }
}
Version Not Found
{
"errors": [
{
"message": "Component version not found",
"extensions": {
"code": "VERSION_NOT_FOUND",
"version": 5
},
"path": ["preview"]
}
],
"data": { "preview": null }
}
Preview Resource Deleted
{
"errors": [
{
"message": "Preview resource not found or deleted",
"extensions": {
"code": "PREVIEW_NOT_FOUND"
},
"path": ["preview"]
}
],
"data": { "preview": null }
}