Visual Editing for interactive live previews
To dramatically improve the content creation experience for your authors, configure your Hydrogen front end and Sanity Studio for Visual Editing.
In short, Visual Editing works by dynamically setting the React Loader configured by hydrogen-sanity
to query for draft documents using the previewDrafts
perspective.
As well as include "stega encoding" to return information about the origin of the content and create interactive overlays so that authors can use the front end as a means to navigate the content. Most commonly, this is all done inside the Presentation tool.
hydrogen-sanity
readme for all installation steps if you need further guidance.To support visual editing in the current environment, pass a preview
configuration to the loader. In order to enable or disable visual editing for the current request, you'll need to provide a condition like checking whether or not the user's session has the project ID set.
If a preview
option isn't provided, then the current environment won't have visual editing setup. In some cases, you may want to only enable visual editing in a preview deployment.
You can check for preview mode using the sanity
context's preview?.enabled
value.
The token value here will be created later in this lesson. Previews won't work without it.
createSanityLoader
in your server.ts
file to configure server-side data fetching when preview mode is activated.const sanity = createSanityLoader({ // ...other settings
// Optionally, configure Visual Editing in the current environment... preview: env.SANITY_API_TOKEN ? { // ...and the condition for when to enable it enabled: session.get("projectId") === env.SANITY_PROJECT_ID, token: env.SANITY_API_TOKEN, studioUrl: "http://localhost:3333", } : undefined,});
Use this to determine whether to render a preprepared VisualEditing
component is rendered.
// ...other importsimport {VisualEditing} from 'hydrogen-sanity/visual-editing'
export async function loader({context}: LoaderArgs) { return defer({ // ... other loader data preview: context.sanity.preview, })}
export default function App() { const data = useLoaderData<typeof loader>()
return ( <> <Outlet /> {data.preview ? <VisualEditing /> : null} </> )}
The Presentation tool can be configured to visit a specific route to potentially enable preview mode. Within the hydrogen-sanity
package is a prepared route which will use the Sanity Client you configured in server.ts
to check for a secret that Presentation has prepared – and if matched – write the projectId
to the current user's session.
export {loader} from 'hydrogen-sanity/preview/route'
Hydrogen comes preconfigured with strict Content Security Policy (CSP) restrictions – one of which prevents the front end from rendering inside an iframe.
These settings are a single string sent along from the server in the header of the response. The code below modifies a section to allow a specific URL to render the page within an iframe.
entry.server.tsx
file to allow the Studio to view the site in an iframe when in development.// ./app/entry.server.tsx
// ...all other importsimport type {AppLoadContext, EntryContext} from '@shopify/remix-oxygen';
export default async function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext, loadContext: AppLoadContext,) { const projectId = loadContext.env.SANITY_PROJECT_ID;
const {nonce, header, NonceProvider} = createContentSecurityPolicy({ // Include Sanity domains in the CSP defaultSrc: ['https://cdn.sanity.io', 'https://lh3.googleusercontent.com'], connectSrc: [ `https://${projectId}.api.sanity.io`, `wss://${projectId}.api.sanity.io`, ], frameAncestors: [`http://localhost:3333`, `'self'`], });
Note that frameAncestors
will need to be updated with any URL that hosts the Studio and will show the Hydrogen front-end inside the Presentation tool's iframe. Consider using an environment variable to provide the origin at runtime.
In this section you'll setup the Presentation tool – a unique plugin with a Studio tool where authors can browse the website as a way to navigate your Sanity content.
You can associate documents with routes on the website so that content creators can more quickly jump from documents to any page on the front end. These are called "locations" in Presentation.
import { defineLocations } from "sanity/presentation";
export const locations = { product: defineLocations({ select: { title: 'store.title', slug: 'store.slug.current', }, resolve: (doc) => ({ locations: [ { title: doc?.title || 'Untitled', href: `/products/${doc?.slug}`, }, {title: 'Products', href: `/products`}, ], }), }), }
Back in your Sanity Studio project, update the plugins inside sanity.config.ts
to include the Presentation tool – with one configuration setting to visit the resource route setup earlier in this exercise.
sanity.config.ts
file// ./sanity.config.ts
// Add these importsimport { presentationTool } from "sanity/presentation";import { locations } from "./presentation/locations";
export default defineConfig({ // ...all other settings
plugins: [ presentationTool({ previewUrl: { origin: "http://localhost:3000", preview: "/", previewMode: { // This should match the route you created earlier enable: "/resource/preview", }, }, }), // ..all other plugins ],});
The origin is necessary if you are deploying your storefront and Studio on different domains. If you provide one, the other options are relative to that domain. Like the origin in your Content Security Policy, you might consider using an environment variable you can assign at runtime.
A viewer token is required for server-side fetching of draft content as well as "stega encoding" for Visual Editing.
.env
file in your Hydrogen application.SANITY_API_TOKEN="sk..."
You will also need to allow communication from your front end to the Content Lake by adding its domain as a CORS origin.
http://localhost:3000
to the CORS origins in your Sanity project settings at sanity.io/manage.Now visit the Presentation tool in your Studio. You should be redirected to the home page. Navigate to a product that contains some Sanity content and you should be able to click-to-edit and watch previews of draft content rendering in your front end in real time.
Note that because some of the content – such as product names – were rendered from Shopify, you won't be able to click and edit them.