storyblok / storyblok-react

React SDK for Storyblok CMS
MIT License
121 stars 35 forks source link

Extending StoryblokComponent with pageData prop for context passing #1253

Open edvinasjurele opened 1 day ago

edvinasjurele commented 1 day ago

Description

Context / Problem

As we know, NextJS does not have native data passing mechanism from server to server component (something like Astro has Astro.locals, which is available in components, middleware and endpoints), hence we can only use prop-drilling in NextJS.

Here is a quick comparison of the mechanisms available in Astro vs NextJS: image

NextJS has reasons for not having this, and it's due to segmentation (explained here https://github.com/vercel/next.js/issues/43704#issuecomment-2090798307). For the same reasons, you can't access the URL / Pathname information in server components.

Suggested solution or improvement

Workaround with Storyblok approach

However, not having the possibility to pass data server-to-server can be a challenge, there is a pattern how can we overcome this, and here is how?

There might be users who want to implement the following use cases (just a few examples):

  1. Fetch the data from CMS about the page theme - light / dark - and switch descendant components look based on that
  2. Fetch pricing plan data for the whole page and pass down the information for various deeper components to consume so data like price or discount, can be used in multiple components without the need to fetch them in each instance.
  3. When rendering f.e. [...slug]/page.tsx), resolve the "locale" for the page and pass it down to Storyblok components so they can react to it, hence pre-render language switcher choices according to business logic.

The pattern we use is to always pass pageData prop to StoryblokComponent:

<Section
  heading={headingBlok && <StoryblokComponent blok={headingBlok} pageData={pageData} />}
  subheading={
    subheadingBlok && <StoryblokComponent blok={subheadingBlok} pageData={pageData} />
  }
  {...storyblokEditable(blok)}
>
  {content &&
    content.map((contentBlok) => (
      <StoryblokComponent blok={contentBlok} key={contentBlok._uid} pageData={pageData} />
    ))}
</Section>

So we always know that the other component has access to that contextual data. The only thing is that developers should not forget to pass that data down each time in component implementation - this might have the potential to be enforced by wrapping StoryblokComponent with custom logic and using it, or the component in this SDK could do so.

Closing remarks

This may not necessarily be something to implement or do in the code, as it is possible to implement in projects even now, because StoryblokComponent implementation allows us to pass any rest props down to components, but I am sure this pattern could be outlined somewhere in the documentation of the SDK as an example of how to achieve one or the other sophisticated pattern.

Additional context

No response

Validations

edvinasjurele commented 1 day ago

@edodusi as per your request, raising this issue to share how we do. Hopefully it will be helpful.

edvinasjurele commented 1 day ago

The best thing that this approach works with both client and server components :)

edodusi commented 11 hours ago

Thanks @edvinasjurele!