JSPM

  • Created
  • Published
  • Downloads 2359709
  • Score
    100M100P100Q205332F
  • License MIT

Thread-safe Helmet for React 16+ and friends

Package Exports

  • react-helmet-async
  • react-helmet-async/lib/client

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (react-helmet-async) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

react-helmet-async

This package is a fork of React Helmet. <Helmet> usage is synonymous, but SSR is radically different.

react-helmet relies on react-side-effect, which is not thread-safe. If you are doing anything asynchronous on the server, you need Helmet to to encapsulate data on a per-request basis, this package does just that.

Usage

The main way that this package differs from react-helmet is that it requires using a Provider to encapsulate Helmet state for your React tree. If you use libraries like Redux or Apollo, you are already familiar with this paradigm:

import HelmetProvider from 'react-helmet-async/lib/Provider';

const app = (
  <Provider>
    <App>
      <Helmet>
        <title>Hello World</title>
        <link rel="canonical" href="https://www.tacobell.com/" />
      </Helmet>
      <h1>Hello World</h1>
    </App>
  </Provider>
);

On the server, we will no longer use static methods to extract state. react-side-effect exposed a .rewind() method, which Helmet used when calling Helmet.renderStatic(). Instead, we are going to pass a context prop to HelmetProvider, which will hold our state specific to each request.

import HelmetProvider from 'react-helmet-async/lib/Provider';

const helmetContext = {};

const app = (
  <Provider context={helmetContext}>
    <App>
      <Helmet>
        <title>Hello World</title>
        <link rel="canonical" href="https://www.tacobell.com/" />
      </Helmet>
      <h1>Hello World</h1>
    </App>
  </Provider>
);

const { helmet } = helmetContext;

// helmet.title.toString() etc...

Streams

This package only works with streaming if your <head> data is output outside of renderToNodeStream(). This is possible if your data hydration method already parses your React tree. Example:

import through from 'through';
import { renderToNodeStream } from 'react-dom/server';
import { getDataFromTree } from 'react-apollo';
import Helmet from 'react-helmet-async';
import HelmetProvider from 'react-helmet-async/lib/Provider';
import template from 'server/template';

const helmetContext = {};

const app = (
  <Provider context={helmetContext}>
    <App>
      <Helmet>
        <title>Hello World</title>
        <link rel="canonical" href="https://www.tacobell.com/" />
      </Helmet>
      <h1>Hello World</h1>
    </App>
  </Provider>
);

await getDataFromTree(app);

const [header, footer] = template({
  helmet: helmetContext.helmet,
});

res.status(200);
res.write(header);
renderToNodeStream(app)
  .pipe(
    through(
      function write(data) {
        this.queue(data);
      },
      function end() {
        this.queue(footer);
        this.queue(null);
      }
    )
  )
  .pipe(res);