Fast initial load of dynamic pages with Meteor and React

With the changes in the new version of Lighthouse, we saw our pages drastically dropped in score as compared to the last version (version 6 vs version 5)

This forced us to take a look again at how we are loading our pages and worked hard during the last couple of weeks to change the structure of our meteor pages.

Here are some learnings that might help other users of Meteor and React

  1. Server Side Rendering
  1. Inline CSS
  • @wildhart technique of inline css: Pre-rendered landing pages with Critical CSS
  • react-router to identify and cache the inline-css of different routes (although the pages are dynamic, each route normally displays the same page structure (i.e. the same critical css); used staticContext for cases where a page structure changes depending on some variable)
  1. Async load CSS
  1. Proper React hydration without re-rendering (for many, no white-screen flicker)
  • react-router for the staticrouter context: https://reacttraining.com/react-router/web/example/static-router
  • fast-render-like data handling: we did not use fast-render in this case but the idea is the same. When generating the SSR page, we attached the data from the static context to the generated HTML page. During react hydration, the data is being used to hydrate the page and not query from the server

Sample pages:


Feedback welcome so we can further improve our pages

6 Likes

“Inline CSS”

—> i always use styled-components for react-apps. it can collect style sheets to send along with the initial page server side render.

As a pro-tip: in chromes network tab, find the request of the html document, select it and click on preview. Does it look right?

if yes, stylesheets are correctly sent along,

Would sending the main CSS bundle with HTTP/2 push headers be just as good?

I am personally wary of using CSS in JS, because of the runtime overhead, even if it speeds up page load. (Even though I do use it on my main app - mostly because Material-UI uses JSS.)

I guess that would be just as good

but with respect to “performance last”: css in js is usually much cleaner, easier, more flexible and maintainable then pure css (or even scss). I would value that much higher than some runtime overhead (unless you are some tech giant where every milisecond matters)

We did a lot of tests with HTTP/2 multiplexing (although with JS and not CSS): No JS files concatenation for production bundle

But for all tests that we did, for the smaller packages, the best it can do is just around the performance of one single bundle.

We do not adhere to CSS in JS. It does not fit how we look at how CSS works

Just learned something new. Thanks :+1:

3 Likes

My pattern is still very modular - I have an SCSS file right next to my component definitions in the file hierarchy, it’s just included in a main SCSS bundle instead of the JS, and all delivered at once instead of piecemeal through JS. Every file has a “root” class/id to keep things modular. CSS in JS always seemed like overkill to me.

@rjdavid Do you use Apollo? Can you share your SSR code? The front-end source code for the web page looks great

We are not using Apollo.

Cannot share our SSR-code for now. Will require some cleaning as it includes some app-specific code. Although the structure is as I have shared above

@rjdavid meteor1.10.2 + apollo 3 + ssr

Check it out. The only problem with reloading and blinking is that it’s a perfect starter kit

From experience, the blinking problem (white-screen flicker) is caused by the following in a component:

React render -> componentDidMount -> query data -> callback with results -> setState with the results -> re-render

We solved this issue by ensuring that componentDidMount will not run any data query function because the state already has the data from the SSR page. Without data query, no setState, no re-render. Therefore, it is just hydration in the initial load.

1 Like

That’s how we did as well.

You need to make sure the SSRed page looks exactly the same as what the client will initially render which is done by injecting the data in the page and using it for the initial load.

@rjdavid Thank you very much. I will study it again, according to your idea

Thank you very much, maybe my project is a little different, if it is a single template will not find this problem

Updated our handling to use PWA + AMP