Use Hygraph with Visual Editor
Visual Editor has first-class support for two-way syncing content between Hygraph and Visual Editor.
# Example configuration
Visual Editor has a tight integration to support Hygraph as one or more of your content sources. This guide covers how to configure Visual Editor to enable two-way data synchronization with your Hygraph projects.
Here is an example configuration that uses Next.js as the site framework, uses a Page
model to represent pages, and creates a custom sitemap to override the default sitemap behavior:
// stackbit.config.ts
import {
defineStackbitConfig,
SiteMapEntry,
DocumentStringLikeFieldNonLocalized
} from '@stackbit/types';
import { HygraphContentSource } from "@stackbit/cms-hygraph";
// Use defineStackbitConfig to allow Typescript checks
export default defineStackbitConfig({
stackbitVersion: "~0.6.0",
ssgName: "nextjs",
nodeVersion: "20",
// useESM: true is required for HygraphContentSource
// as it has ESM only dependencies.
useESM: true,
contentSources: [
new HygraphContentSource({
// Hygraph project ID.
// Can be found in project settings screen in Hygraph Studio.
projectId: process.env.HYGRAPH_PROJECT_ID!,
// Hygraph project region.
// Can be found in project settings screen in Hygraph Studio.
// Example: US-WEST-2
region: process.env.HYGRAPH_REGION!,
// Hygraph project environment. Default: master
environment: process.env.HYGRAPH_ENVIRONMENT!,
// Hygraph content API endpoint URL.
// Must match the configured region.
// Example:
// https://{REGION}.cdn.hygraph.com/content/{HASH}/{ENVIRONMENT}
contentApi: process.env.HYGRAPH_ENDPOINT!,
// Hygraph management API endpoint URL.
// Must match the configured region.
// Example:
// https://management-{REGION}.hygraph.com/graphql
managementApi: process.env.HYGRAPH_MANAGEMENT_API!,
// The management token.
managementToken: process.env.HYGRAPH_MANAGEMENT_TOKEN!
})
],
// This marks the Hygraph's "Page" model as a "page" models in the Visual-Editor
modelExtensions: [
{ name: 'Page', type: 'page' }
],
// The sitemap maps between the "Page" entries and their url paths
// allowing easy navigation between site pages in the Visual-Editor.
sitemap: ({ documents }): SiteMapEntry[] => {
// The "documents" include all the entries from Hygraph.
return documents.reduce(
(sitemap: SiteMapEntry[], document): SiteMapEntry[] => {
// Filter documents that match "Page" model name
// This is the model ID in Hygraph.
if (!['Page'].includes(document.modelName)) {
return sitemap;
}
const slugField = document.fields.slug as DocumentStringLikeFieldNonLocalized;
if (!slugField || !slugField.value) {
return sitemap;
}
sitemap.push({
urlPath: `/${slugField.value.replace(/^\/|\/$/g, '')}`,
document: document
});
return sitemap;
}
);
}
});
Notice the following:
HygraphContentSource
is being loaded from the@stackbit/cms-hygraph
which must be installed first as a dev-dependency. This package is not needed in your production site.- Hygraph is being configured using local environment variables. Visual Editor will automatically load a
.env
file in the root of your project. - The example assumes a model with an ID of
Page
that is being used to store page data in Hygraph. That content type has a field with nameslug
that determines the URL path at which the page is made available.
# Prerequisites
To be able to work with Hygraph, you must first have the following:
A Hygraph project with content.
The proper API keys that can access your content. For more info check out the options section below.
Installed
@stackbit/cms-hygraph
package as a development dependency. (We also recommend@stackbit/types
to help with configuration.)npm install -D @stackbit/types @stackbit/cms-hygraph
# Usage
// stackbit.config.ts
import { defineStackbitConfig } from '@stackbit/types';
import { HygraphContentSource } from "@stackbit/cms-hygraph";
export default defineStackbitConfig({
stackbitVersion: "~0.6.0",
// ... other config options
useESM: true,
contentSources: [
new HygraphContentSource({
projectId: "...",
region: "...",
environment: "...",
contentApi: "...",
managementApi: "...",
managementToken: "...",
})
]
});
# Options
The following are all required options, unless noted:
projectId
: The Hygraph project ID, found in the project settings screen in Hygraph Studio.region
: The Hygraph project region, found in the project settings screen in Hygraph Studio.environment
: The Hygraph project environment. (Default: master)contentApi
: The Hygraph content API endpoint URL. Must match the configured region.Example:
https://{REGION}.cdn.hygraph.com/content/{HASH}/{ENVIRONMENT}
managementApi
: The Hygraph Management API endpoint URL. Must match the configured region.Example:
https://management-{REGION}.hygraph.com/graphql
managementToken
: The management token. The management token must have the following configuration:Content API section:
- Default stage for content delivery: Draft
- Content permissions enabled for all models, all stages, and all locales
Management API - The management API must include 21 permissions:
- Read existing environments
- Read existing models
- Read existing components
- Read existing fields
- Read existing enumerations
- Read existing entries
- Read remote sources
- Read stages
- Read locales
- Can see schema view
- Update existing non-published entries
- Update published entries
- Publish non-published entries
- Create new entries
- Delete existing entries
- Create new webhooks
- Read existing webhooks
- Update existing webhooks
- Delete an existing webhook
- Can see Role & Permissions Settings
- Can read content permissions
- (Optional for debugging) Can use the playground
componentQueryNestingLevel
(Optional): Defines the nesting level in GraphQL queries for cyclic components. This helps reduce query complexity for large content schemas. (Default: 3)entriesFilter
: Filters entries fetched byHygraphContentSource
.The
entriesFilter
is applied as the “where” argument in the GraphQL query used to fetch entries. Since each Hygraph model has different “where” properties,entriesFilter
is an object mapping a model’s API ID to its filter value.Warning
Ensure that filtered-out entries are not referenced by entries that pass the filter. Otherwise, the Visual Editor will display “Field is missing or inaccessible” errors where a reference field points to a filtered-out entry. If your content architecture does not allow for this, consider using the
permissionsForDocument
method instackbit.config.ts
.The
entriesFilter
applies only to the “where” clause, so it cannot be used to filter by locales or stages.Example:
The following filter retrieves:
- Page entries where
enumField
is “foo” andtitle
contains “homepage.” - Post entries where
author.name
is “john.”
entriesFilter: { Page: '{ enumField: foo, title_contains: "homepage" }', Post: '{ author: { name: "john" } }' }
For more info visit Hygraph Filtering Documentation
- Page entries where
debugGraphQLQueries
(Optional): Logs GraphQL queries, including complexity details for entry queries.To enable “debug” level logs, run
stackbit dev
with--log-level=debug
flag:stackbit dev --log-level=debug
splitEntryRequestsPerModel
(Optional): Splits GraphQL entry requests by model to reduce query complexity when the content schema is too complex for querying multiple models in a single request.When this flag is set to
false
(the default), a single GraphQL query includes all models, and as pages of entries are fetched, the number of models will decrease:query { Pages(stage: DRAFT, first: 100, skip: 0) { ...PageFragment } Posts(stage: DRAFT, first: 100, skip: 0) { ...PostFragment } Authors(stage: DRAFT, first: 100, skip: 0) { ...AuthorFragment } }
When this flag is set to
true
, each GraphQL query will contain a single model, and entries will be fetched serially, one model at a time:Request 1:
query { Pages(stage: DRAFT, first: 100, skip: 0) { ...PageFragment } }
Request 2:
query { Posts(stage: DRAFT, first: 100, skip: 0) { ...PageFragment } }
# Store sensitive values
Sensitive values can be stored in a .env
file, which will then be available when Visual Editor configuration file is loaded.
# .env
HYGRAPH_PROJECT_ID=
HYGRAPH_REGION=
HYGRAPH_ENVIRONMENT=master
HYGRAPH_ENDPOINT=
HYGRAPH_MANAGEMENT_API=
HYGRAPH_MANAGEMENT_TOKEN=
# Local development
Hygraph Content Source uses webhooks to synchronize content. For local development with Visual Editor, the easiest method is to use an ngrok tunnel and pass the tunnel URL when running stackbit dev
.
# Install ngrok
If you don’t have ngrok installed, please follow the ngrok quick start guide to install it.
# Start ngrok agent
With your development server running in one terminal window, open a second terminal and start the ngrok agent on port 8090
:
ngrok http 8090
Once ngrok starts, it will print a publicly-accessible URL in the form of https://xyz.ngrok.app or https://xyz.ngrok.io, which can be used to access localhost:8090. Keep this terminal window open with ngrok running.
Example output:
Session Status online
...
Forwarding http://xyz.ngrok.app -> http://localhost:8090
Forwarding https://xyz.ngrok.app -> http://localhost:8090
# Start stackbit dev
Open a third terminal window and install the Visual Editor CLI if you haven’t already:
npm i -g @stackbit/cli
Run stackbit dev
with the --csi-webhook-url
argument set to your ngrok public URL, ending with the /_stackbit/onWebhook
path:
stackbit dev --csi-webhook-url=https://<REPLACE>.ngrok.app/_stackbit/onWebhook
Once stackbit dev
starts, open the http://localhost:8090/_stackbit link printed in the terminal to open the Visual Editor.
# Content type inference
The Hygraph Content Source infers all Hygraph models to be Visual Editor data
models, unless otherwise specified.
Therefore, models that represent website pages must be extended to have type: 'page'
using the modelExtensions
property.
# Use multiple projects or environments
The contentSources
property is an array of content sources that are all pulled together with Visual Editor. You can add another content source by instantiating another class as another item in the array, while using separate environment variables for the options.
// stackbit.config.ts
import { HygraphContentSource } from "@stackbit/cms-hygraph";
export default {
contentSources: [
new HygraphContentSource({
projectId: process.env.HYGRAPH_PROJECT_ID_01!,
region: process.env.HYGRAPH_REGION_01!,
environment: process.env.HYGRAPH_ENVIRONMENT_01!,
contentApi: process.env.HYGRAPH_ENDPOINT_01!,
managementApi: process.env.HYGRAPH_MANAGEMENT_API_01!,
managementToken: process.env.HYGRAPH_MANAGEMENT_TOKEN_01!
}),
new HygraphContentSource({
projectId: process.env.HYGRAPH_PROJECT_ID_02!,
region: process.env.HYGRAPH_REGION_02!,
environment: process.env.HYGRAPH_ENVIRONMENT_02!,
contentApi: process.env.HYGRAPH_ENDPOINT_02!,
managementApi: process.env.HYGRAPH_MANAGEMENT_API_02!,
managementToken: process.env.HYGRAPH_MANAGEMENT_TOKEN_02!
})
]
// ...
};
Did you find this doc useful?
Your feedback helps us improve our docs.