Live preview
Hygraph Studio's Live Preview feature allows you to preview content in your frontend before it's published.
#What you can do
- Preview your work before it goes live.
#Limitations
- Live Preview is not compatible with native mobile applications.
- Live Preview does not support Visual Editing.
- Requests count against rate limits and usage of your project.
#Add a preview widget
Live preview - Add to content model
To use live preview, you must first add a preview widget to a model in your schema.
-
Navigate to the Schema builder.
-
Select a model to add the preview widget
-
Click on the Sidebar tab, located at the top of the screen.
-
Select the
Preview
widget from the right sidebar. -
Complete the
Preview name
and theURL template
fields.Pro TipThis is where you can add your own website URL and specify how the preview slug should be constructed from field values in the content entry.
Click here to learn how to define your own preview URLs.
-
Click
Add
to save.
You can now use the live preview feature
#How to define a URL template
You need to have a frontend already set up to be able to configure previews.
You can also add localhost URLs so you can preview your work locally before even publishing your website.
The exact way to build a URL template for previews depends on your website's URL structure.
Typically, you will start with your domain, then a section such as a "blog". Finally, you will use handlebars notation to include a field that is unique to the content you want to preview, to use as an identifier - usually a unique slug or the ID.
For example:
https://preview.your-domain.com/blog/{slug}
or
https://your-domain.com/blog/{slug}?preview=true
The preview URL structure will vary based on your project. What matters most here is the {slug}
token, which will dynamically add the slug of what you are editing to the preview URL.
As soon as you type in the open curly brace, the system will list all the available fields for you.
We also recommend adding a secret token to the URL, which only your app and Hygraph will know.
You can also see these options in the Available fields screen section. You can use more than one of the available fields, if needed.
Your website needs to support live preview to get the most out of this feature. Always make sure that, when in preview mode, your website pulls content from the DRAFT
stage and not from PUBLISHED
. Otherwise your previews won't include any unpublished changes and you'll only see data from the PUBLISHED
stage.
Please note that this feature simply creates a shortcut to link to that content directly from your Content Editor. This way, you can see how your draft data will look like in your app before publishing it and without leaving Hygraph.
#Delete a preview widget
Delete a preview widget
- Navigate to the Schema builder.
- Select the model that contains the preview widget that you want to delete.
- Click on the context menu for the widget, and select
Remove
.
#Use live preview
Follow these steps to preview your content:
- Navigate to the Content editor and select a content view.
- Click on an existing entry to edit it.
- Click
Open live preview
on the right sidebar. The preview will display to the right of the content editing screen. - Make changes to the content entry and then click
Save & preview
.
If more than one preview link is configured, you can use the dropdown menu right on the sidebar to select one.
Live preview - Dropdown selection
Our live preview feature will show you a side-by-side content preview in the Hygraph content form:
Live preview
To update the preview after making additional changes, make sure to click Save & preview
.
To preview content, it needs to exist at least in the DRAFT
stage.
On entries that haven't been saved yet you won't be able to see the Open live preview
button on the right sidebar.
#Switch view
At the top of your preview you will find view switcher options that allow previewing the website for different screen sizes.
Switch view
Simply click on one of the three buttons to select a preview size. The available options are: desktop, mobile, and full width.
#Troubleshooting
#CSP or security header issues
Once the preview is set up, the live preview panel may display a Refused to connect
message, or the console may show something like Refused to frame 'https://example.com/'
because an ancestor violates the following Content Security Policy (CSP) directive: frame-ancestors 'self'
.
CSP or security header issues
If this happens, it could be due to a strict security configuration (Security Header or Content Security Policy) on your website that prevents other websites from embedding it.
To resolve this, contact your development or security team to adjust your website's security configuration.
To fix this issue, we need to verify what's causing it. Follow these steps:
-
Navigate to the Network tab in your browser console.
-
Search for the page you are trying to see.
-
Under the Headers subtab, click on the Response Headers.
-
Determine whether the security configuration matches one of the options below:
- If the
X-Frame-Options
header is set, remove it from your application.
OR
- If the Content-Security-Policy header doesn't include the Hygraph domain, you need to add it. It should look similar to this:
frame-ancestors 'self' https://*.hygraph.com
.
- If the
#Stale data
If you're dealing with stale data, you need to make sure your application is reading data from the DRAFT
stage, and that you don't have a caching layer when you're in preview mode.
Without this, your requests will be cached and so you will always see stale data in the UI.
Here are some recommendations:
API permissions
Make sure you define the permissions for our API correctly. You can create a token for preview and another for production.
Preview token
In your Hygraph project, go to Project Settings > Access > Permanent Auth Tokens
and create a new token for preview. You need to select DRAFT
as the default stage.
Once on the token page, make sure you initialize default permissions.
Production token
Repeat the process to create a token, but this time select PUBLISHED
as the default stage. Make sure to also initialize default permissions.
Now, after you create the tokens, you need to update your application so you can use them.
If you are using Next.js, we recommend using Draft Mode with App Router, and for Pages Router, you can also use Draft Mode, but with a different setup.
For Remix, here's a complete guide on how to set up preview mode.
Below, you can also find guides on how to implement it for different frameworks.
#Content API endpoint
We recommend using the High Performance Content API.
While new projects can only see this endpoint, old ones can see - and could be using - the legacy endpoint.
You can find the High Performance Content API endpoint in Project Settings > Access > API Access
.
Click here to learn more about our high performance endpoint.
#Frontend implementation
To be able to use side-by-side visual preview in your frontend implementation, you have to query the GraphQL endpoint for your page in the DRAFT
stage rather than the PUBLISHED
stage.
Ways to query the DRAFT stage
- Create a URL for your web app that queries the
DRAFT
stage of your endpoint. This could look like:https://preview.yourwebsite.com
. You could also use a query parameter on the URL, like this:https://yourwebsite.com?preview=true
. - Create a permission on another content API endpoint that by default returns the
DRAFT
stage in the context of preview. Use that in the web app you want to do preview with. - Change from
PUBLISHED
toDRAFT
in your app based on a preview context inside your frontend.
If your app is fully SSR (server side rendered), make it query the DRAFT
stage and turn off all cache while in the preview iFrame inside Hygraph.
#Next.js
In Next.js we have the concept of draftMode
from the next/headers
package. This mode sets a cookie in the browser to enable itself.
In modern Next.js (13+), the draftMode
cookie is set in such a way as to not allow use of the cookie in iFrames. In the following code, the workaround allows for the setting of the cookie with draftMode
, but then modifies the cookie's sameSite
value to none
to allow it to operate inside the Live Preview iFrame.
import { draftMode, cookies } from 'next/headers';import { redirect } from 'next/navigation';export async function GET(request) {const { searchParams } = new URL(request.url);const previewToken = searchParams.get('previewToken');const slug = searchParams.get('slug');// Check for a slug and for a preview tokenif (!previewToken || !slug) {return new Response('Invalid token', { status: 401 });}// Get the slug from Hygraph to ensure we don't run into redirect loopsconst res = await fetch(process.env.HYGRAPH_ENDPOINT, {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({query: `query SinglePage($slug: String!) {page(where: { slug: $slug }, stage: DRAFT) {slug}}`,variables: { slug },}),});// Return the dataconst { data } = await res.json();// If the data returns in undefined or in the wrong shape, return an errorif (!data || !data.page) {return new Response('Invalid slug', { status: 401 });}// Workaround for https://github.com/vercel/next.js/issues/49927// Enable draft mode as usualdraftMode().enable();// Update the cookie and set same site to none so we can render it inside Hygraphconst cookieStore = cookies();const cookie = cookieStore.get('__prerender_bypass');cookies().set({name: '__prerender_bypass',value: cookie?.value,httpOnly: true,path: '/',secure: true,sameSite: 'none',});redirect(`/${data.page.slug}`);}
Once draftMode
is set up you can amend your GraphQL queries so they can query for the DRAFT
or PUBLISHED
stages.
If you prefer, to make things easier so you don't need to update all your queries, you can also use the strategy we shared before, of having two tokens. If isEnabled
is true, you can conditionally change the token.
In /app/page.tsx
:
import { request } from 'graphql-request';import { draftMode } from 'next/headers';// isEnabled is true if the cookie has been set.// See: https://nextjs.org/docs/app/building-your-application/configuring/draft-modeconst { isEnabled } = draftMode();const query = `query Page($slug: String!, $stage: Stage! = PUBLISHED) {page(where: { slug: $slug }, stage: $stage) {titledescription}})`;const variables = {stage: isEnabled ? 'DRAFT' : 'PUBLISHED',slug: '/about',};const { data: page } = await request(HYGRAPH_ENDPOINT, query, variables);
This is a simplified example to get the basics across. See our SKNCRE Cosmetics Shop Starter for a full implementation.
Next preview URL structure
The preview URL you set up in the sidebar configuration of your schema would look like this:
https://yourwebsite.com/api/draft?secret=MY_SECRET_TOKEN&slug={slug}
Please note that if you follow the Next guide linked here, they will require you to set a secret token.
The /api/draft
endpoint will redirect you back to the original URL but with the preview cookie set up.
#Nuxt
Nuxt has a similar setup to Next.js and can put itself in preview mode as well. The below example is the simplest version. You will see this approach used by many CMS implementations.
There are more comprehensive ways to set Nuxt 3 into preview mode. Read about those in their documentation.
Create a Nuxt plugin in the plugins folder like so: /plugins/review.ts
. Nuxt will automatically read it once it starts up.
export default defineNuxtPlugin((nuxtApp) => {const route = useRoute();const preview = route.query.preview && route.query.preview === 'true';if (preview) {nuxtApp.hook('page:finish', () => {refreshNuxtData();});}return { provide: { preview } };});
This plugin looks at the query string ?preview=true
and based on that provides a global preview variable to the whole app.
The variable $preview
is now available in all components.
In /pages/about.vue
, you can do the following to decide what stage you want to query:
<script setup>import { request } from "graphql-request";// See previous code block for how to enable $previewconst { $preview } = useNuxtApp();const query = `query Page($slug: String!, $stage: Stage! = PUBLISHED) {page(where: { slug: $slug }, stage: $stage) {titledescription}})const variables = {"stage": $preview ? "DRAFT" : "PUBLISHED","slug": "/about"}const { data: page } = await request(HYGRAPH_ENDPOINT,query,variables);</script><template><h1>{{ page.title }}</h1><p>{{ page.description }}</p></template>
This is a simplified example to get the basics across. See our SKNCRE Cosmetics Shop Starter for a full implementation.
Nuxt preview URL structure
The preview URL you set up in the sidebar configuration of your schema would look like this:
https://yourwebsite.com/{slug}?preview=true
#Astro
Astro is generally focused on SSG rendering (static pages) and due to its incredible flexibility in terms of using frontend frameworks, there is no official “preview mode”.
To make it work with Hygraph:
- Use
output: 'server'
to render the Astro site inSSR
mode - Set an
ENV
variable for preview totrue
. For example:ASTRO_USE_PREVIEW
and based on that set your stage fromPUBLISHED
toDRAFT
.
As the site runs in SSR
mode it will always query the DRAFT
endpoint for fresh data.
In astro.config.mjs
:
import { defineConfig } from 'astro/config';import vercel from '@astrojs/vercel/serverless';export default defineConfig({output: 'server',adapter: vercel(),});
You can choose other adapters as well, like: node, Netlify, or Cloudflare.
In /src/pages/page.astro
:
---import { request } from "graphql-request";const isPreview = import.meta.env.ASTRO_USE_PREVIEW === 'yes';const query = `query Page($slug: String!, $stage: Stage! = PUBLISHED) {page(where: { slug: $slug }, stage: $stage) {titledescription}})`const variables = {"stage": $preview ? "DRAFT" : "PUBLISHED","slug": "/about"}const { data: page } = await request(HYGRAPH_ENDPOINT,query,variables);---<html><head><title>{page.title}</title></head><body><main><article><h1>{page.title}</h1><p>{page.description}</p></article></main></body></html>
This is a simplified example to get the basics across. See our SKNCRE Cosmetics Shop Starter for a full implementation.
Astro preview URL structure
Since you cannot set Astro to a preview mode like Next and Nuxt, what you will do is make it read from the DRAFT
stage and set an ENV
variable in the build system that says to query DRAFT
. You should also configure the output: 'server'
setting in the config.
You will then configure your preview URL in the sidebar by creating a URL for your web app that queries the DRAFT
stage of your endpoint. This could look like this: https://preview.yourwebsite.com
or it could have a query parameter in the URL like this: https://yourwebsite.com?preview=true
.
Preview URL structure example:
https://preview.yourwebsite.com/{slug}