> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metabind.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Preview Links

> Token-based access to draft content and components

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`:

```javascript theme={null}
headers: {
  'preview-token': 'YOUR_PREVIEW_TOKEN',
  'Content-Type': 'application/json'
}
```

For WebSocket subscriptions:

```javascript theme={null}
connectionParams: {
  previewToken: 'YOUR_PREVIEW_TOKEN'
}
```

## Preview Query

```graphql theme={null}
query GetPreview($token: String!, $version: Int) {
  preview(token: $token, version: $version) {
    ... on ComponentPreview {
      componentId
      componentName
      component {
        id
        name
        title
        compiled
        schema
      }
      resolvedRef {
        package
        dependencies
      }
    }
    ... on ContentPreview {
      contentId
      contentName
      contentTypeName
      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):

```graphql theme={null}
query GetComponentPreview($token: String!) {
  preview(token: $token) {
    ... on ComponentPreview {
      componentId
      component {
        compiled
        schema
      }
      resolvedRef {
        package
        dependencies
      }
    }
  }
}
```

Response:

```json theme={null}
{
  "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:

```graphql theme={null}
query GetComponentVersion($token: String!, $version: Int!) {
  preview(token: $token, version: $version) {
    ... on ComponentPreview {
      componentId
      component {
        compiled
      }
      resolvedRef {
        package
        dependencies
      }
    }
  }
}
```

Variables:

```json theme={null}
{
  "token": "abc123def456",
  "version": 2
}
```

## Content Preview

Fetch a content preview:

```graphql theme={null}
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:

```graphql theme={null}
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
        }
      }
    }
  }
}
```

<Note>
  Subscriptions are only useful for draft components and unpublished content, as published versions are immutable.
</Note>

## Client Implementation

### React Hook Example

```javascript theme={null}
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

```javascript theme={null}
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 === 'DELETED') {
      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 100KB WebSocket limit:

1. The `component` or `content` field will be `null`
2. The `resolvedRef` is always included
3. Fetch the full data separately using the component/content ID

```javascript theme={null}
if (!update.preview.component) {
  // Component exceeded 100KB
  const component = await fetchComponent(update.preview.componentId);
  refreshComponentPreview(component, update.preview.resolvedRef);
} else {
  refreshComponentPreview(update.preview.component, update.preview.resolvedRef);
}
```

## Error Handling

### Invalid Token

```json theme={null}
{
  "errors": [
    {
      "message": "Invalid or expired preview token",
      "extensions": {
        "code": "INVALID_PREVIEW_TOKEN"
      },
      "path": ["preview"]
    }
  ],
  "data": { "preview": null }
}
```

### Version Not Found

```json theme={null}
{
  "errors": [
    {
      "message": "Component version not found",
      "extensions": {
        "code": "VERSION_NOT_FOUND",
        "version": 5
      },
      "path": ["preview"]
    }
  ],
  "data": { "preview": null }
}
```

### Preview Resource Deleted

```json theme={null}
{
  "errors": [
    {
      "message": "Preview resource not found or deleted",
      "extensions": {
        "code": "PREVIEW_NOT_FOUND"
      },
      "path": ["preview"]
    }
  ],
  "data": { "preview": null }
}
```
