Looking at this further, there is a need to extend/create a new boilerplate that will support renderToPipeableStream
of React 18
Here is a quick reproduction where SSR does not work:
- still using
Meteor.call()
- Meteor 2.14
- Reach 18.2
- React Router 6.21
- using Suspense and lazy
- using
renderToString()
The goal is to support the following through SSR and code-splitting:
- loading performance
- better UX
- SEO
It looks like it works because the app works after hydration. Disabling javascript in the browser shows the real status of the SSR:
- Suspense and lazy did not work in SSR when not yet cached by the server
renderToString()
is not meant to wait for data
Potential solution:
- Make data fetching Suspense-enabled, i.e., throw promises when data is not yet ready
- Support
renderToPipeableStream()
by forking the following Meteor packages:
server-render
boilerplate-generator
webapp
I will post an update here if this will work. Do you have other ideas/comments/feedback?
While studying renderToPipeableStream()
, it becomes clear that React 18 expects to start hydration on document
(therefore the entire HTML document) instead of an element inside <body>
.
This means that the boilerplate template for React 18 SSR must also be JSX.
Good News!
SSR works with the following:
- Meteor 2.14
- Using
Meteor.callAsync()
- Using
- React Router 6.21
- React 18.3.0-canary-c5b937576-20231219
- Suspense
- lazy
renderToNodeStream()
use()
- use – React (as mentioned by @radekmie above)
The example in the above branch has a component and data loaded using Suspense and nested lazy
loaded components.
Caveats:
- Still using
renderToNodeStream()
, which has been deprecated in React 18
use()
hook is still in the canary channel (expected to arrive in 18.3) - can be used in production by pinning the canary version
The use()
hook simplifies everything with Meteor.callAsync()
and Suspense.
const { links } = use(Meteor.callAsync('getLinks'));
To Do:
Since renderToNodeStream()
is deprecated, there is still a need to support renderToPipeableStream()
moving forward. There are two important things to do to make this possible:
- Support a JSX boilerplate-generator template because hydration now happens on
document
instead of an HTML element. This requires allowing a custom template to generate the HTML output. Either Meteor adds a custom template for React or allows the developer to add one. - Allow access to the
response
parameter accessible byrenderToPipeableStream()
Ideally, these features will be available through server-render
package.
So, I’ve just proven myself wrong. With the solution above, it’s possible to even make this work with class components with data fetching happening in the constructor()
when defining the component state
variables