All Projects → dunglas → React Esi

dunglas / React Esi

Licence: mit
React ESI: Blazing-fast Server-Side Rendering for React and Next.js

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to React Esi

Laravel Responsecache
Speed up a Laravel app by caching the entire response
Stars: ✭ 1,874 (+248.98%)
Mutual labels:  cache, varnish, performance
Next Boost
Add a cache layer for server-side-rendered pages with stale-while-revalidate. Can be considered as an implementation of next.js's Incremental Static Regeneration which works with getServerSideProps.
Stars: ✭ 239 (-55.49%)
Mutual labels:  cache, nextjs, ssr
Bigcache
Efficient cache for gigabytes of data written in Go.
Stars: ✭ 5,304 (+887.71%)
Mutual labels:  hacktoberfest, cache, performance
Kubectl Flame
Kubectl plugin for effortless profiling on kubernetes
Stars: ✭ 297 (-44.69%)
Mutual labels:  hacktoberfest, performance
React Keycloak
React/React Native/NextJS/Razzle components for Keycloak
Stars: ✭ 281 (-47.67%)
Mutual labels:  hacktoberfest, nextjs
Laravel Varnish
Making Varnish and Laravel play nice together
Stars: ✭ 291 (-45.81%)
Mutual labels:  varnish, performance
Kinto.js
An Offline-First JavaScript Client for Kinto.
Stars: ✭ 268 (-50.09%)
Mutual labels:  hacktoberfest, cache
Guzzle Cache Middleware
A HTTP Cache for Guzzle 6. It's a simple Middleware to be added in the HandlerStack.
Stars: ✭ 325 (-39.48%)
Mutual labels:  cache, performance
Hint
💡 A hinting engine for the web
Stars: ✭ 3,280 (+510.8%)
Mutual labels:  hacktoberfest, performance
Predator
A powerful open-source platform for load testing APIs.
Stars: ✭ 356 (-33.71%)
Mutual labels:  hacktoberfest, performance
Ava
Node.js test runner that lets you develop with confidence 🚀
Stars: ✭ 19,458 (+3523.46%)
Mutual labels:  hacktoberfest, performance
React Static Tweets
Extremely fast static renderer for tweets.
Stars: ✭ 278 (-48.23%)
Mutual labels:  nextjs, ssr
React Storefront
Build and deploy e-commerce progressive web apps (PWAs) in record time.
Stars: ✭ 275 (-48.79%)
Mutual labels:  nextjs, ssr
React Storefront
React Storefront - PWA for eCommerce. 100% offline, platform agnostic, headless, Magento 2 supported. Always Open Source, Apache-2.0 license. Join us as contributor ([email protected]).
Stars: ✭ 292 (-45.62%)
Mutual labels:  nextjs, ssr
Next Super Performance
The case of partial hydration (with Next and Preact)
Stars: ✭ 272 (-49.35%)
Mutual labels:  nextjs, performance
Wipe Modules
🗑️ Easily remove the node_modules folder of non-active projects
Stars: ✭ 304 (-43.39%)
Mutual labels:  hacktoberfest, performance
Awesome Wp Speed Up
Plugins and resources to speed up and optimize your WordPress site.
Stars: ✭ 375 (-30.17%)
Mutual labels:  varnish, performance
Memento
Memento is a development-only tool that caches HTTP calls once they have been executed.
Stars: ✭ 380 (-29.24%)
Mutual labels:  hacktoberfest, cache
React Head
⛑ SSR-ready Document Head tag management for React 16+
Stars: ✭ 262 (-51.21%)
Mutual labels:  hacktoberfest, ssr
Ristretto
A high performance memory-bound Go cache
Stars: ✭ 3,584 (+567.41%)
Mutual labels:  cache, performance

React ESI: Blazing-fast Server-Side Rendering for React and Next.js

Build Status Coverage Status npm version MIT Licence

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.

ESI example

Schema from The Varnish Book

Discover React ESI in depth with this presentation

Examples

Install

Using Yarn:

$ yarn add react-esi

Or using NPM:

$ npm install react-esi

Usage

React ESI provides a convenient Higher Order Component that will:

  • replace the wrapped component by an ESI tag server-side (don't worry React ESI also provides the tooling to generate the corresponding fragment);
  • render the wrapped component client-side, and feed it with the server-side computed props (if any).

React ESI automatically calls a static async method named getInitialProps() to populate the initial props of the component. Server-side, this method can access to the HTTP request and response, for instance, to set the Cache-Control header, or some cache tags.

These props returned by getInitialProps() will also be injected in the server-side generated HTML (in a <script> tag). Client-side the component will reuse the props coming from the server (the method will not be called a second time). If the method hasn't been called server-side, then it will be called client-side the first time the component is mounted.

The Higher Order Component

// pages/index.js
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>
);
// components/MyFragment.js
import React from 'react';

export default class MyFragment extends React.Component {
  render() {
    return (
      <section>
        <h1>A fragment that can have its own TTL</h1>

        <div>{this.props.greeting /* access to the props as usual */}</div>
        <div>{this.props.dataFromAnAPI}</div>
      </section>
    );
  }

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

      // Simulate a delay (call to a remote service such as a web API)
      setTimeout(
        () =>
          resolve({
            ...props, // Props coming from index.js, passed through the internal URL
            dataFromAnAPI: 'Hello there'
          }),
        2000
      );
    });
  }
}

The initial props must be serializable using JSON.stringify(). Beware Map, Set and Symbol!

Note: for convenience, getInitialProps() has the same signature than the Next.js one. However, it's a totally independent and standalone implementation (you don't need Next.js to use it).

Serving the Fragments

To serve the fragments, React ESI provides a ready to use controller compatible with Express:

// server.js
import express from 'express';
import { path, serveFragment } from 'react-esi/lib/server';

const server = express();
server.use((req, res, next) => {
  // Send the Surrogate-Control header to announce ESI support to proxies (optional with Varnish, depending of your config)
  res.set('Surrogate-Control', 'content="ESI/1.0"');
  next();
});

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) 
);

// ...
// Other Express routes come here

server.listen(80);

Alternatively, here is a full example using a Next.js server:

// server.js
import express from 'express';
import next from 'next';
import { path, serveFragment } from 'react-esi/lib/server';

const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express();
  server.use((req, res, next) => {
    // Send the Surrogate-Control header to announce ESI support to proxies (optional with Varnish)
    res.set('Surrogate-Control', 'content="ESI/1.0"');
    next();
  });

  server.get(path, (req, res) =>
    serveFragment(req, res, fragmentID => require(`./components/${fragmentID}`).default)
  );
  server.get('*', handle); // Next.js routes

  server.listen(port, err => {
    if (err) throw err;
    console.log(`> Ready on http://localhost:${port}`);
  });
});

Features

  • Support Varnish, Cloudflare Workers, Akamai, Fastly and any other cache systems having ESI support
  • Written in TypeScript
  • Next.js-friendly API

Environment Variables

React ESI can be configured using environment variables:

  • REACT_ESI_SECRET: a secret key used to sign the fragment URL (default to a random string, it's highly recommended to set it to prevent problems when the server restart, or when using multiple servers)
  • REACT_ESI_PATH: the internal path used to generate the fragment, should not be exposed publicly (default: /_fragment)

Passing Attributes to the <esi:include> Element

To pass attributes to the <esi:include> element generated by React ESI, pass a prop having the following structure to the HOC:

{
  esi: {
    attrs: {
      alt: "Alternative text",
      onerror: "continue"
    }
  }
}

Troubleshooting

The Cache is Never Hit

By default, most cache proxies, including Varnish, never serve a response from the cache if the request contains a cookie. If you test using localhost or a similar local domain, clear all pre-existing cookies for this origin. If the cookies are expected (e.g.: Google Analytics or ad cookies), then you must configure properly your cache proxy to ignore them. Here are some examples for Varnish.

Design Considerations

To allow the client-side app to reuse the props fetched or computed server-side, React ESI injects <script> tags containing them in the ESI fragments. After the assembling of the page by the cache server, these script tags end up mixed with the legit HTML. These tags are automatically removed from the DOM before the rendering phase.

Going Further

React ESI plays very well with advanced cache strategies including:

  • Cache invalidation (purge) with cache tags (Varnish / Cloudflare)
  • Warming the cache when data change in the persistence layer (Varnish)

Give them a try!

Vue.js / Nuxt

We love Vue and Nuxt as much as React and Next, so we're a currently porting React ESI for this platform. Contact us if you want to help!

Credits

Created by Kévin Dunglas. Sponsored by Les-Tilleuls.coop.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].