How Live Editing in Storyblok works
The live editing setup for Storyblok differs a little from the standard documentation in Storyblok. Many concepts of course still remain because the core functionality is provided by Storyblok itself. However for our setup we have 2 requirements that their tutorials and default solution do not fulfill:
The solution should work with server components. We therefore can not re-render components client side with updated live editing data from Storyblok. This ensures the javascript bundle stays relatively low.
Our solution should work with our custom GraphQL schema.
For a general technical overview from Storyblok, please visit this link:
https://www.storyblok.com/docs/guide/essentials/visual-editor
Data flow
The data flow in summary is:
-
A content editor changes some field, adds or removes a blok in the visual editor inside Storyblok.
-
Our site that is loaded in the visual editor inside an iframe receives a postMessage containing the JSON data of the whole updated story (not just the change).
- This data is not saved by Storyblok, it only stores this data if you either save as draft or publish the story.
-
Our site captures this postMessage JSON (client side) and posts it to our GraphQL gateway which temporarily stores it (in memory) and returns a previewId for that entry.
-
We then store this previewId in a cookie and ask NextJS to revalidate the page.
-
NextJS refetches the pages with the previewId as additional parameter.
-
The GraphQL resolver for the page content refetches the page and merges it with the temporarily stored preview JSON and transforms the data into the desired GraphQL schema.
-
The page is now rebuild server side by NextJS with the update from the content editor, and he/she can view the updated page within the iFrame.
- Because of how NextJS revalidation works, this does not result in a page refresh, and therefore feels quite instant.
Story Selection
In order for the visual editor to make the React components selectable, they need to have some data set as data attributes to an HTML element. This includes: the story id, the space id the blok id, and the blok type name. In the end, the HTML will need to look something like the following in order for it to work for Storyblok:
<div
data-blok-c="{name: <component type>, id: <story id>, space: <space id>, uid: <blok UUID>}"
data-blok-uid="<story id>-<blok UUID>"
>
...
</div>
We can get the space and the story id from the query parameters which are given to the iFrame.
<iframe
src="https://demo.machinabox.com/my-page/?_storyblok=<story-id>&_storyblok_tk[space_id]=<space=id>..."
></iframe>
We can get the blok id and the blok type from the GraphQL fragment in our queries. Note that in order for this to work, we need to always query the __typename and the id for a Storyblok component if we want to make it selectable inside the preview environment.
type Props = {
data: MyStoryblokFragment; // must include `__typename` and `id`
};
export const MyStoryblokComponent = ({ data }: Props) => (
<div {...componentProps(data)}>
... some content
</div>
);
The componentProps function sets 2 other more generic properties, namely a data-component-id and a data-component-type.
The idea behind this is that these React components should not be directly linked to Storyblok, since Evolve should be CMS agnostic.
The Storyblok attributes data-blok-c and data-blok-uid are then set in a useEffect hook inside a top-level Storyblok React component called StoryblokStory.tsx.
This hook queries all elements client side containing a data-component-id and adds the two specific Storyblok attributes to it using the query parameters from the iFrame.
Summary for developers
So in summary, please note that if you are developing a Storyblok component ensure that you:
- Include the
__typenameandidin the Storyblok fragment. - Add the Storyblok HTML attributes using the
componentPropsfunction with the Storyblok fragment as input.
Debugging Live Editing locally
By default the visual editor is connected to the test environment. However, when something might be broken and we need to figure out what is going on, we can open the visual editor with a local host connection.
For this to work, localhost needs to run on https instead of http. So next to a regular developer process running on port 3000 (from pnpm dev), you need to run a https proxy on port 3010:
See the README in the frontend sites repository:
Then you can select the localhost:3010 environment in Storyblok in the top left corner of the visual editor.