Replies: 1 comment
-
|
With the context above, I'd also like to float the idea of introducing a "Strict Mode" version for using |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Note
I have been wrestling with RSC patterns for a few weeks while migrating our storyblok-driven app to Next.js App Router. I'm putting out this RFC to ensure it resonates and that there isn't some fundamental piece of information I am missing. Please let me know your thoughts! I also recognize this is written mostly through the lens through using
@storyblok/react/rscwith Next.js App Router, but in theory this idea applies to anything using RSC rendering.Summary
The
@storyblok/react/rscpackage works well for basic use cases, but the documentation is missing critical guidance for advanced RSC patterns. This makes it challenging to use true server components (async, server-only APIs) within Storyblok-powered applications.Package: @storyblok/react
Current Documentation: Next.js App Router Guide
Glossary
'use client'directive (can be nested inside RSCs)storyblokInit: Configures component registry for Storyblok renderingStoryblokServerComponent: Renders dynamic Storyblok content using components from registryWhat's Working Well
✅
StoryblokServerComponentworks in both server and client contexts✅ Basic integration with Next.js App Router
✅ Examples are clear for simple use cases
✅ RSCs can be used above the
StoryblokStoryrendering treeWhat's Missing
1. Guidance on True Server Components
The current examples of "RSC"s (readme, example apps) only show isomorphic components (pure rendering, no server-only features):
What's missing: Examples showing async components with server-only APIs:
The critical challenge: When
StoryblokServerComponentis used inside a'use client'boundary, it performs runtime lookup in the client bundle's component registry. This creates a cascading limitation:asynccomponents can be nested (they're not in the client registry)fs, database, etc.) can be used in nested contentWhy this is particularly problematic:
Even if you only use
StoryblokServerComponentinside a'use client'boundary once, you've now opted out of server-only RSCs for that entire subtree. With many components, tracking which components can nest where becomes unwieldy and error-prone.This is technically correct React behavior (client components cannot import or render server-only code), but the implications for Storyblok component architecture are not documented.
The solution: NEVER use
StoryblokServerComponentinside a'use client'boundary. Instead, always use the composition pattern (RSC wrapper + client component) to keepStoryblokServerComponentin RSC context.Suggested Documentation Additions
1. Composition Pattern for Client Components
Problematic pattern (limits nested content to client bundle only):
Recommended pattern (allows all component types in nested content):
Why this matters:
Key principle:
StoryblokServerComponentshould ONLY be used in RSC context (files without'use client'). If you need client-side interactivity, use composition patterns to keepStoryblokServerComponentin the RSC wrapper (see Next.js docs: Interleaving Server and Client Components).2. Component Registration Strategy
Next.js App Router has three rendering phases, each using different bundles:
Each bundle maintains its own component registry when
storyblokInitis called. The key insight: both registries are used during server-side rendering, but at different phases.Recommended approach:
Why this matters:
'use client'directives and create proper RSC boundaries during the RSC render phaseWhy client components MUST be in the server bundle's registry:
This is a common point of confusion: "If client components only render during SSR/CSR, why include them in
storyblokInitServer?"Answer: During the RSC render phase, when
StoryblokServerComponentencounters a client component name (e.g.,"button"), it needs to:'use client'directive)'use client'directive and create a "hole" (placeholder) for the client componentWithout the client component in the server registry:
With the client component in the server registry:
Key insight: The server bundle doesn't render client components (that happens in SSR/hydration), but it needs to know about them to create the proper RSC boundaries.
3. Component Categorization
As alluded to above, there are three types of components: server-only RSCs, isomorphic, and client. It would be helpful to document the three types of components and how they should be registered:
Server-Only RSC:
asyncfs, database, etc.)StoryblokServerComponentinside a'use client'boundaryRegistration: Only in server bundle (storyblokInitServer)
Isomorphic:
asyncRegistration: Both server and client bundles (direct import in server, dynamic import in client)
Client:
'use client'directiveasyncRegistration: Both server and client bundles (direct import in server for RSC boundary detection, dynamic import in client for code-splitting)
Summary
The
@storyblok/react/rscpackage works great for basic use cases: RSCs can wrap<StoryblokStory />and this package will render your story appropriately. However it falls over if you try to register RSCs with storyblok rendering, particularly nested RSCs. Adding documentation for these advanced patterns would help developers:Key Takeaways:
storyblokInitregistries are used during server-side rendering (at different phases)StoryblokServerComponentcannot look up the component, React cannot detect'use client'directives, and no RSC "holes" are created'use client'boundariesRelated Resources:
Beta Was this translation helpful? Give feedback.
All reactions