Preserving State from the pre-render#

Also known as: "the state problem". You can ignore this section if your app doesn't make XHR/Websocket requests.

  1. server-side render happens for an app that makes XHR/Websocket requests
  2. client receives and renders that server-side rendered HTML+JS+CSS from the server
  3. the client JavaScript app (React, Angular, Ember, Preact, Vue, etc.) initializes
  4. the client has no knowledge that the server already made these requests so it makes those XHR/Websocket requests again that take ~500ms to complete

Will this cause a "flash" or "blink" while the JavaScript app renders a zero/empty state for the in-flight XHR/Websocket request?

Yes, apps that fetch any remote data via AJAX or websockets will have this problem. There are 2 different solutions:

  1. Ajax-Preload XHR monkey patch (included by default)
    • By default, Headless-Render-API.com caches GET requests made via XHR (not websockets) during the prerender process and serializes it into the HTML (you can see a base64 string of your data if you cmd/ctr+f your HTML source for "Ajax-Preload").
    • This converts those 500ms requests to 1ms (they're still async, but no network request is made)
    • This works because Headless-Render-API.com monkey patches XHR. It's a sensible default solution to this "state problem" (disable with HTTP header: Prerender-Disable-Ajax-Preload: true) but the next solution is better.
  2. window.__PRELOADED_STATE__ or window.__PRELOADED_STATE_PLAIN__
    • If you have well organized state management (Redux, MobX), or using websockets, or making POST requests we recommend using the special variable window.__PRELOADED_STATE__ or window.__PRELOADED_STATE_PLAIN__. Headless-Render-API.com specifically looks for this variable and will serialize it into the HTML if it exists. It allows you to do something like this (plain React example):
      // React side note: use componentWillMount instead of didMount to avoid
      // "React attempted to reuse markup in a container but the checksum was invalid"
      // (componentDidMount is not called during renderToString)
      componentWillMount() {
       if (window.__PRELOADED_STATE__) {
         // reuse the state from the server
         this.setState(window.__PRELOADED_STATE__);
       } else {
         // make an async request and save
         // to the __PRELOADED_STATE__ var so
         // Headless-Render-API.com will serialize into the HTML
         window.fetch('/my-ajax-endpoint')
           .then(response => response.json())
           .then(json => {
             this.setState(json);
             window.__PRELOADED_STATE__ = json;
           });
       }
      }
      
    • if you set the __PRELOADED_STATE__ or window.__PRELOADED_STATE_PLAIN__ variable, we assume you're managing all state and so we automatically disable the Ajax-Preload monkey patch.
    • window.__PRELOADED_STATE__ and window.__PRELOADED_STATE_PLAIN__ do the same thing except the former does base64encode(JSON.stringify(content)) and the latter only does JSON.stringify.