React ESI: Blazing Fast SSR

React ESI: Blazing Fast SSR

React ESI is a super powerful cache library for vanilla React and Next.js applications, that can make highly dynamic applications as fast as static sites. It provides a straightforward way to boost your application's performance by storing fragments of server-side rendered pages in edge cache servers. It means that after the first rendering, fragments of your pages will be served in a few milliseconds by servers close to your end users! It's a very efficient way to improve the performance and the SEO of your websites; and to dramatically reduce both your hosting costs and the energy consumption of these applications. Help the planet, use React ESI!

Because it is built on top of the Edge Side Includes (ESI) W3C specification, React ESI natively supports most of the well-known cloud cache providers including Cloudflare Workers, Akamai and Fastly. Of course, React ESI also supports the open source Varnish cache server that you can use in your own infrastructure for free (configuration example).

Also, React ESI allows to specify a different Time To Live (TTL) per React component and to generate the corresponding HTML asynchronously using a secure (signed) URL. The cache server fetches and stores in the cache all the needed fragments (the HTML corresponding to every React component), builds the final page and sends it to the browser. React ESI also allows components to (re-)render client-side without any specific configuration.

E66449b8260b07a1cf51c5ab5eaa8180?s=128

Kévin Dunglas

April 23, 2019
Tweet

Transcript

  1. 2.

    React ESI @dunglas Kévin Dunglas ❏ Les-Tilleuls.coop’s Founder ❏ Symfony

    Core Team (PHP ) ❏ API Platform and Mercure creator @dunglas
  2. 3.

    React ESI @dunglas ✍ Self-managed since 2011 ‐ 36 people,

    1,000% in 6 years ➡ jobs@les-tilleuls.coop
  3. 6.

    React ESI @dunglas Single Page Apps are So Cool ❏

    Update only what is needed (web components) ❏ No full page reload ❏ No « white flash » ❏ Smaller network payloads (JSON vs HTML) ❏ Rich UX
  4. 10.

    React ESI @dunglas But SPAs Have Major Drawbacks: SEO «

    Currently, it's difficult to process JavaScript and not all search engine crawlers are able to process it successfully or immediately.
 In the future, we hope that this problem can be fixed, but in the meantime […]. »
  5. 15.

    React ESI @dunglas SSR Benefits ❏ Typical SEO (like in

    PHP) ❏ Fast Initial Display (theoretically) ❏ SPA/PWA when fully loaded ❏ One code base, client-side/server-side (theoretically… again)
  6. 16.

    React ESI @dunglas SSR with React import React from "react";

    import ReactDOMServer from "react-dom/server"; const Index = () => ( <div> <h1>Hello</h1> </div> ); const pageContent = ReactDOMServer.renderToString(<Index />);
  7. 18.

    React ESI @dunglas SSR Drawbacks: Performance ❏ All the page

    must be computed, every time, before sending it to the client ❏ The server must wait for all data needed to display the page ❏ The cache can be set only per full page, not per component
  8. 23.

    React ESI @dunglas Edge Side Includes ❏ Kind of server-side

    iframe ❏ Page composition is usually handled by a cache server (edge server) ❏ Every fragment can have a different TTL ❏ Use standard HTTP cache headers
  9. 24.

    React ESI @dunglas Edge Side Includes ❏ Created by Akamai

    ❏ Supported by Varnish (open source), Squid, CloudFlare, Fastly, Akamai… ❏ Open format, submitted to the W3C in 2001 (not accepted) ❏ Implemented in Symfony since 2011
  10. 25.

    React ESI @dunglas 
 Just Add the Tag, the Cache

    Server Will Handle Everything Else Could be implemented directly using Node, or CloudFlare Workers
  11. 27.

    React ESI @dunglas React ESI ❏ Server-side, replace React components

    you want by an ESI tag ❏ Specify a different TTL per component ❏ The corresponding HTML is generated asynchronously ❏ The cache server fetches and stores in its cache all the needed fragments
  12. 29.

    React ESI @dunglas React ESI: the Higher Order Component import

    React from 'react'; import withESI from 'react-esi'; import MyFragment from 'components/MyFragment'; const MyFragmentESI = withESI(MyFragment, 'MyFragment'); // The second parameter is an unique ID identifying this fragment. // If you use different instances of the same component, use a different ID per instance. const Index = () => ( <div> <h1>React ESI demo app</h1> <MyFragmentESI greeting="Hello!" /> </div> );
  13. 30.

    React ESI @dunglas A Fragment export default class MyFragment extends

    React.Component { render() { return ( <section> <h1>A fragment {this.props.greeting /* access to the props as usual */}</h1> </section> ); } }
  14. 31.

    React ESI @dunglas Setting a TTL export default class MyFragment

    extends React.Component { render() { return ( <section>…</section> ); } static async getInitialProps({ props, req, res }) { return new Promise(resolve => { // Set a TTL for this fragment if (res) res.set('Cache-Control', 's-maxage=60, max-age=30'); }); } }
  15. 32.

    React ESI @dunglas Computing initial props export default class MyFragment

    extends React.Component { render() {/*..*/} static async getInitialProps({ props, req, res }) { return new Promise(resolve => { setTimeout( // Simulate a delay (call to a remote service such as a web API) () => resolve({ ...props, // Props coming from the parent, passed through the internal URL dataFromAnAPI: 'Hello there’ }), 2000 ); }); } }
  16. 33.

    React ESI @dunglas Serving The Fragments (internal URL) import express

    from 'express'; import { path, serveFragment } from 'react-esi/lib/server'; const server = express(); server.get(path, (req, res) => // "path" default to /_fragment, change it using the REACT_ESI_PATH env var serveFragment( req, res, // "fragmentID" is the second parameter passed to the "WithESI" HOC, the root component used for this fragment must be returned fragmentID => require(`./components/${fragmentID}`).default) );
  17. 35.

    React ESI @dunglas State Reconciliation ❏ Prevent fetching data clients-side

    and server-side ❏ During the 1st request (server-side), data are fetched and initial props are computed ❏ The initial props are serialized and injected in the server-side HTML (in a <script> tag) ❏ Client-side, React ESI automatically finds and reuses these props when initializing the components