-
Notifications
You must be signed in to change notification settings - Fork 134
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
RFC: Next.js App Router support in @faust/blocks package #1619
Comments
Just to confirm - in this case, specific blocks could still leverage client React functionality and thus be included in the bundle, but in doing so, the remainder of the blocks in the tree would still remain RSC with the relevant benefits. Am I understanding that correctly? |
Hey @jordanmaslyn thank you for asking. If you are using the What instead you need to use for app-router is to avoid using any of the What you get is the same experience but without sending the components to the client. |
Basically we avoid using any of |
@theodesp I totally understand that! What I am asking is if I had a block that needed interactivity (e.g. an Accordion block) but wanted RSC for all of the others, would that be possible in this new paradigm using So the Accordion would be included in the bundle but all others would remain RSC and thus not be included in the bundle. Is that accurate? |
@jordanmaslyn thats right. You can use |
@theodesp how would this affect the generated js bundle? ( Currently, because blocks are loaded into the provider in |
@justlevine thank you for asking. None of the CoreBlocksRSC will use 'use client' directive so are not loaded as client side components so the bundle size stays flat. Since the We may have to create a new npm export under the /rsc folder to avoid any side-effects
The only problem that I see now is when using the dot (.) notation with blocks will be problematic We will have to provide an alternative solution to tackle this. |
We may have to introduce a different way to export block components something like that: function CoreCode(props: CoreCodeFragmentProps) {...}
const fragments = {..}
const config = {
name: 'CoreCode',
};
const displayName = 'CoreCode';
export default { Component: CoreCode, config, displayName, fragments }; This is annoying but the spec is very fussy. |
Regarding the above issue with the dot (.) notation with blocks I will have to make some experiments since it may cause issues when mixing client and server components. I will post my updates here. |
While it's probably outside of the scope to actually fix in this PR, I'm hoping whatever solution you land on don't make it harder to support dynamically-imported client side blocks in the future 🤞 |
I'm not sure how this is feasible at the moment. So far I've encountered the above issue when using Dynamic components: Cannot access CoreParagraph.then.then on the server. You cannot dot into a client module from a server component. You can only pass the imported name through. I also tried using this approach:
But it looks like that WebPack did not create a separate chuck for it but included it in the page.js assets. Not sure if it's possible to have dynamically-imported client blocks this way. |
Correct, because the current client-based provider loads the entire component in the Perhaps the effort fix/work around the dot-notation will open a path to solve it, but I doubt it. However, we can hopefully avoid making it worse 🤞 |
@justlevine I found a workaround with the dot-notation and I've updated the spec. Let me know what you think. export { default as Component } from './CoreParagraph.js';
export * from './CoreParagraphMeta.js'; // contains config and fragments available in the server side Exported module signature: Before CoreParagraph: Function() { // Client Export + React Component
config: Object,
fragments: Object
}, Here dot notation fails to work on the server: Cannot access CoreParagraph.config on the server. You cannot dot into a client module from a server component. You can only pass the imported name through. After CoreParagraph: Object [Module] {
Component: [Getter], // Client Export + React Component
config: [Getter], // Server Export
fragments: [Getter] // Server Export
}, Here dot notation works fine on the server: CoreParagraph.config
{ name: 'CoreParagraph' } |
@theodesp looks good to me. As far as I can tell there's no real additional coupling in the signature, which means it would be trivial to update |
As it has been a while. I'm going to close this issue until we can revisit in roadmap discussions. |
@ChrisWiegman can you clarify? Other than #1624 what work towards making blocks work server-side still needs to be done/ reevaluated for inclusion? |
We have recently done some research into seeing how we can support the Next.js App Router in
@faust/blocks
package and for Gutenberg.Problem Statement
Next.js recently introduced a new feature called "App Router" in which a new directory, called "app", is used to create and route pages. These pages work differently than the file based pages we see in the "pages" directory. Instead of a single file containing the React presentation component and the SSR/SSG counterpart, like getServerSideProps or getStaticProps, the App Router makes use of several files to create one presentation. Since Faust uses these SSR/SSG counterparts to fetch data, authenticate, etc. this poses an issue for supporting the App Router with the current implementations in Faust. Additionally, with Next.js shifting to React Server Components, we will need to come up with solutions for fetching data, authenticating, etc, all on the server within RSCs (React Server Components).
Proposal
Since supporting React Server Components requires to avoid using React specific hooks and providers we can propose the following additions to the
@faust/blocks
packages to accommodate rendering blocks as RSC:CoreBlocks
For example we use the
useBlocksTheme
hook in the CoreBlocks. Instead we should pass thetheme
parameter as a property:Before
After
This makes the component an RSC so it can be rendered on the server.
Provide a new component to render RSC components without using
WordPressBlocksProvider
andWordPressBlocksViewer
. The new component calledBlocksViewerRSC
will accept all the required properties it self , eliminating the need for a React Provider which marks the components as Client Only:BlocksViewerRSC
takes the following properties:To facilitate the smooth usage of the Core blocks as RSC component we re-export the Core blocks under a new name
CoreBlocksRSC
:Before
After
The
CoreBlocksRSC
contain all the original blocks but they are specifically used with theBlocksViewerRSC
component.Since Client Side component exports are not visible in the Server Side then we cannot access the config or any of the component metadata using the dot (.) operator. SEE: Dot notation client component breaks consuming RSC. vercel/next.js#51593.
To avoid those issues we need to separate those options both from the client side and the server side. This is how we propose the Block Component Exports to be.
Here is the
CoreParagraph
Block written in the new format as a client component:// CoreParagraph.js
Now we separate the config and fragments into a separate exported file which is available on the server:
// CoreParagraphMeta.js
And in the index.js we export both modules under the following convention:
Now when you importing the component you need to import the whole module (Component and Meta properties):
Notice the convention here:
Component: is the React component that we want to render. It could be either client or a server component.
The next export
export * from './CoreParagraphMeta.js';
is an object with the usual Block metadata like fragments and config. This should be a server component only since we need to read this information on the server.The final exported module will be the following object:
With this approach we are able to use both client side and server components from within
BlocksViewerRSC
without any issues.Fallback block
Instead of passing the Fallback block as a parameter to
BlocksViewerRSC
you can add this to the blocklist under the special property name:fallBackBlock
. For example:The
BlocksViewerRSC
will try to detect if this block exists and try to use it when rendering a default block.User Experience
When you use the provided
BlocksViewerRSC
you should be able to see the original blocks rendered as RSC component so they are not shipped to the client.Caveats
BlocksViewerRSC
since theBlocksViewerRSC
is rendered on the server and not client side code (useEffect) can run during that time.registerFaustBlock
function to convert a React Component to Block. https://faustjs.org/tutorial/react-components-to-gutenberg-blocksCompatibility Matrix
The following Matrix captures the compatibility of Blocks when using client vs server components between BlocksViewerRSC and WordPressBlocksViewer
WordPressBlocksViewer
for any original CoreBlocks since they work only on the client side. Not compatible with app-router package.BlocksViewerRSC
for any CoreBlocksRSC when using app-router.BlocksViewerRSC
for any client side component as long as you provide the correct export.BlocksViewerRSC
for any server side component as long as you provide the correct export.In other words if you are using the next.js 13 app-router package use
CoreBlocksRSC
otherwise useWordPressBlocksViewer
POC
This branch encompasses a POC of the above proposal.
The text was updated successfully, but these errors were encountered: