Skip to content

Instantly share code, notes, and snippets.

@Ventanas95Dev
Last active October 6, 2024 10:43
Show Gist options
  • Save Ventanas95Dev/2683f50accac68369ef6bdc3fc62e392 to your computer and use it in GitHub Desktop.
Save Ventanas95Dev/2683f50accac68369ef6bdc3fc62e392 to your computer and use it in GitHub Desktop.
Next.js (app router) React server components and Storyblok Preview "real time update" πŸŽ‰
import 'server-only'
import { kv } from '@vercel/kv'
export const getStoryblokStoryPreviewCache = async (slug) => {
try {
const story = await kv.get(slug)
if (story) {
return story
}
} catch (e) {
// Silence is golden
console.log(e)
}
}
Add
experimental: {
serverActions: true,
},
To next.config.js
Add StoryblokPrevieSyncer to your the page.js or where you fetch your storyblok data.
<StoryblokPreviewSyncer
storyId={data?.story?.id}
pathToRevalidate={localeData?.slug + '/' + data?.story?.slug}
/>
Dont forget to also add StoryblokBridgeLoader so i will also reload the preview window on save and publish
Use getStoryblokStoryPreviewCache where you fetch the data. I do it like this.
export const getStory = async ({ slug, locale, preview }) => {
try {
if (preview) {
const story = await getStoryblokStoryPreviewCache(slug)
if (story) {
return { story }
}
}
const { data } = await fetchSB({
path: `cdn/stories/${slug}`,
locale,
preview,
})
return data
} catch (e) {
throw `Storyblok slug "${slug}" not found`
}
}
'use server'
import { kv } from '@vercel/kv'
import { revalidatePath } from 'next/cache'
export async function previewUpdateAction({ story, pathToRevalidate }) {
if (story?.id) {
await kv.set(story?.slug, JSON.stringify(story), { ex: 100 })
if (pathToRevalidate) {
await revalidatePath(pathToRevalidate)
}
}
}
export const registerStoryblokBridge = (id, cb, options) => {
const isServer = typeof window === 'undefined'
const isBridgeLoaded =
!isServer && typeof window.storyblokRegisterEvent !== 'undefined'
const storyId = new URL(window.location?.href).searchParams.get('_storyblok')
const inStory = +storyId === id
if (!isBridgeLoaded || !inStory) {
return
}
if (!id) {
console.warn('Story ID is not defined. Please provide a valid ID.')
return
}
window.storyblokRegisterEvent(() => {
const sbBridge = new window.StoryblokBridge(options)
sbBridge.on(['input', 'published', 'change'], (event) => {
if (event.action === 'input' && event.story.id === id) {
cb(event.story)
}
})
})
}
'use client'
import { useEffect, useTransition } from 'react'
import { registerStoryblokBridge } from './registerStoryblokBridge'
import { previewUpdateAction } from '../../app/actions/previewUpdateAction'
export const StoryblokPreviewSyncer = ({ storyId, pathToRevalidate }) => {
let [isPending, startTransition] = useTransition()
useEffect(() => {
const id = storyId
registerStoryblokBridge(
id,
(story) => {
startTransition(() =>
previewUpdateAction({
story,
pathToRevalidate: pathToRevalidate,
}),
)
},
{},
)
}, [])
return <div />
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment