FastRender 3.0 is here! Now with SSR data hydration helpers

Hey everyone,

I’ve just updated my fork of FastRender to 3.0. It supports Meteor 1.6.1 again and I’ve added some new features to help with different SSR techniques.

The biggest problem I had with SSR in Meteor was data hydration. I saw a few techniques to mitigate this by having either a flash of empty content, or swapping out the entire DOM tree when the data was loaded. Neither felt quite right, to me it was simpler to provide the client with the data it needed to render the view the exact same way the server did. So, FastRender will now automatically track Meteor.subscribe on the server and include that data in the HTML payload.

On the server:

FastRender.onPageLoad(sink => {
    sink.renderIntoElementById('app', renderToString(<App  />))
})

On the client:

FastRender.onPageLoad(async sink => {
	const App = (await import('/imports/components/App/App')).default
	ReactDOM.hydrate(<App />, document.getElementById('app'))
})

This is obviously an opinionated approach to hydration and may not work great for huge amounts of data. I should note that when using this approach, the data payload will no longer be in the <head> but instead at the end of the <body>, after the server-rendered HTML. This is important because it will allow us to render the critical HTML to a stream, and essentially defer the loading of the data and JavaScript bundle. When the bundle loads, the client-side renderer has all of the data it needs to completely hydrate the view.

This doesn’t have support for dynamic components yet, but it looks like it will be doable with react-loadable. Another possibility is to have FastRender track critical-css via something like CSS Modules, so we can defer loading of the monolithic stylesheet and inline the critical path CSS.

Let me know what you think: https://github.com/abecks/meteor-fast-render

The only thing left to do is add support for React’s renderToNodeStream, which looks like it will require a similar technique to what styled-components does (vs. using Meteor EnvironmentVariables like we’re doing now). I’d love some help working out these final features and pushing out a new set of SSR helper APIs for FastRender.

19 Likes

Hey @abecks amazing work. I hope you keep improving this and remove old outdated stuff from the repo.

I will probably integrate your project in my starter app.

Thank you !

I came across this thread - I just came up with a similar idea for tracking subscriptions server side on another thread, and thought I’d share my experience with SSR and renderToNodeStream, since what you are doing seems to match up with what I did. You said this is an opinionated way to do SSR, but I think it would be super easy to create a somewhat idiomatic API to support the way I’ve gotten SSR to work.

My starter project has this stuff encapsulated. The specific part you might be interested in is /server/main.js in the renderToNodeStream branch. It shows how to use node streams to append some data to the end of the output (and how to clean it up on the client side to avoid warnings when hydrating).

You could create a SubscriptionProvider component to use on the server, which would polyfill Meteor.subscribe on the server, and capture all the subscriptions into an array (you have probably worked this out already). Then an implementor would append that data to the output. For faster-render, it could also append any captured data from queries. This could be encapsulated into a method as you mention similar to styled’s interleaveWithNodeStream. The only thing left to figure out then is how to encapsulate cleaning the appended script element from the react DOM tree client side (maybe a simple clean method).

2 Likes

I’m actually thinking about writing a package to encapsulate some of this, maybe call it something like meteor-ssr-toolkit. It wouldn’t completely take care of setting up SSR - I think the app implementor should do some of that, in case they have their own needs. It would provide a set of APIs to make some of this easier to set up. I might also create a new version of React Loadable with a similar API, since all the SSR support in RL assumes webpack, and doesn’t even work with Meteor out of the box (I created a PR, but I doubt it’ll be accepted).

Guys I would be really interested in the outcome of this,to use with viewmodel
I already have a starter project for SSR and indeed having subscription at SSR time is super handy.
You should not have any special things to do to make it compatible, just keep using server-render package behind the scene and I’ll be able to integrate your solutions