-
Notifications
You must be signed in to change notification settings - Fork 17
feat(astro)!: astro live preview now support custom payload #404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: alpha
Are you sure you want to change the base?
Conversation
BREAKING CHANGE: getLiveStory now provides a story and extraData
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is being reviewed by Cursor Bugbot
Details
Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
alexjoverm
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the great work @dipankarmaikap! Just a few quick fixes to go through, other than that looks on point 👍
Additional one - can you also link the issues the PR solves in the the PR description?
Closes #95, closes #96
Change payload variable from extraData to serverData. Sanitization added for the user-provided server payload
@storyblok/astro
storyblok
@storyblok/eslint-config
@storyblok/js
storyblok-js-client
@storyblok/management-api-client
@storyblok/nuxt
@storyblok/react
@storyblok/region-helper
@storyblok/richtext
@storyblok/svelte
@storyblok/vue
commit: |
| r="70"></circle></svg | ||
| > | ||
| </div> | ||
| <LivePreviewSpinner /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems with this you also addressed #97
I am just not sure if there is a way to have live preview already disabled on initialization or should I load frontend app and dispatch something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh wait, it is just a spinner, either shown or hidden, so the toggling is not part of this functionality, isnt it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I have a separate PR coming for #97
| @@ -0,0 +1,71 @@ | |||
| --- | |||
| --- | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: can this be removed?
packages/astro/src/lib/helpers.ts
Outdated
| let story: ISbStoryData | null = null; | ||
| if (Astro && Astro.locals._storyblok_preview_data) { | ||
| story = Astro.locals._storyblok_preview_data; | ||
| interface Payload<T = unknown> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: I think we could make T more specific and rename the function to match its new functionality:
type StoryblokServerData = typeof Astro.props
export async function getPayload<ServerData extends StoryblokServerData = StoryblokServerData, Story extends ISbStoryData = ISbStoryData>({
locals: { _storyblok_preview_data: { story, serverData } }
}: { locals: { _storyblok_preview_data?: { serverData?: ServerData; story?: Story; } } }) {
return { story, serverData };
}
// Correct usage:
interface ServerData {
users?: User[];
}
interface MyStory {
content: { myField: string }
}
const payload = await getPayload<ServerData /* optional */, MyStory /* optional */>(Astro);
const story = payload.story ?? /* ... */
const users = payload.serverData?.users ?? /* ... */
// Usage with type error:
type ServerData = string;
const payload = await getPayload<ServerData /* Type error! */>(Astro);There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just had the thought that introducing getPayload might open the possibility of keeping the original getLiveStory making this a backwards compatible change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would be great since it avoids any breaking changes for existing users. Should we also consider adding a deprecated warning so users can take action on their own?
| const event = new CustomEvent<T>(name, { detail, cancelable: true }); | ||
| document.dispatchEvent(event); | ||
| return event; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: What was your reasoning for introducing a new function instead of adding a third options param to dispatchStoryblokEvent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You’re right, this is internal-only and used once. I’ll switch it to the existing function with an additional parameter. Thanks.
Removed `dispatchCancelableStoryblokEvent` and added a new param in `dispatchStoryblokEvent` introduced `isEditorRequest` function for both internal and end user
…implify the spinner
| && url.searchParams.has('_storyblok_c'); | ||
| // First do a check if its coming from within storyblok | ||
| const editorRequest | ||
| = isEditorRequest(new URL(request.url)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Middleware validation now requires additional parameter
Medium Severity
The middleware now uses isEditorRequest which requires three URL parameters (_storyblok, _storyblok_c, and _storyblok_tk[space_id]), but the previous validation only required two (_storyblok and _storyblok_c). This stricter validation could silently break live preview functionality if editor requests don't always include _storyblok_tk[space_id]. When validation fails, the middleware won't set _storyblok_preview_data, causing the page to fall back to API fetching instead of using live preview data. The PR states "No breaking changes" but this behavioral change could affect existing setups.
Additional Locations (1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dipankarmaikap this functionality would be ideal to have it in the JS SDK. There are some checks there but are hardcoded. Ideally, it would be encapsulated in a utility that can be also exported so the Astro SDK or any other SDK can reuse it
https://github.com/storyblok/monoblok/blob/main/packages/js/src/index.ts#L93
I'll leave it up to you if you want do it as part of this PR, or separately later in the future (which is perfectly fine)
This PR introduces persistent server-side data support for Astro live preview, adds a new
getPayloadAPI and enables per-route and editor-controlled live preview behaviour.Astro live preview can now preserve server-side data across preview updates, enabling more efficient and flexible data handling. It also gives developers and editors finer control over when live preview updates should occur.
What’s new
Persistent server-side data
StoryblokServerData.astro, a new component used to embed sanitized server-side data.This component embeds sanitized server-side data into the page so it can be reused across live preview updates without refetching.
New
getPayloadAPIA new
getPayloadfunction is introduced to support persistent server-side data without changing the existinggetLiveStoryAPI.getLiveStoryremains unchanged and fully backward compatible.The new API returns both live preview story data and persistent server-side data in a single call.
Example:
The returned payload contains:
story– live preview story dataserverData– persistent server-side dataLive preview control per route
Developers can disable live preview behavior on a per-route basis using a meta tag. This is useful for routes where live preview updates are not desired and a full page reload is preferred.
Example:
When live preview is disabled for a route:
Editor-controlled live preview via events
This release also enables editor-facing control over live preview updates by listening to an existing browser event. Teams can build custom UI controls, such as a checkbox or toggle, to allow editors to opt in or out of live preview updates.
Example:
Spinner and loading state considerations
If this feature is used together with a loading spinner or editor-side loading indicator, the spinner logic must account for prevented events. Otherwise, the spinner may remain visible even when live preview updates are cancelled.
Recommended pattern:
This adjustment is only required if consumers actively prevent live preview updates.
isEditorRequesthelperA new
isEditorRequestutility is now exposed for consumers. It allows detecting whether a request originates from the Storyblok editor, which is useful for rendering editor-only UI or widgets that should not appear on the public site.Security
sanitizeJSONutility with test coverage.StoryblokServerDatato sanitize embedded server data.Fixes
Breaking changes
None.
Existing integrations using
getLiveStorycontinue to work without modification. The newgetPayloadAPI is fully opt-in.Note
Enhances Astro live preview with persistent server data and fine-grained control.
StoryblokServerData.astroto embed sanitized server data (via newsanitizeJSON) that persists across live preview updatesgetPayload()to accessstoryandserverData; keepsgetLiveStory()(deprecated) for backward compatibility<meta name="storyblok-live-preview" content="disabled">, dispatch cancelablestoryblok-live-preview-updatingandstoryblok-live-preview-updatedevents, and postserverDataback on updatesisEditorRequest()util and updates middleware to validate editor requests and store{ story, serverData }inlocalsStoryblokServerData.astrosanitizeJSONandisEditorRequest; playground examples for spinner and editor togglealpha,beta,next) and improve script checksWritten by Cursor Bugbot for commit cfbc155. This will update automatically on new commits. Configure here.