airbnb / hypernova

A service for server-side rendering your JavaScript views
MIT License
5.82k stars 207 forks source link

React Helmet server-side rendering? #121

Closed jcruz closed 6 years ago

jcruz commented 6 years ago

I'm using Rails with Hypernova to render a SPA. Is it possible to use Hypernova with React Helmet to manage the document head? The react-helmet server example extracts the helmet content after ReactDOMServer.renderToString. However, Hypernova uses a default export of the renderReact method so I'm not sure how to accomplish this.

jcruz commented 6 years ago

For those wondering how to do this, you need to write your own renderReact method as was done in packages like airbnb/hypernova-aphrodite and airbnb/hypernova-amp.

Here's the code that I've used for React Helmet:

import React from 'react';
import ReactDOM from 'react-dom';
import ReactDOMServer from 'react-dom/server';
import hypernova, { serialize, load } from 'hypernova';
import { Helmet } from 'react-helmet';

const template = ({ helmet, body }) => `
  <!doctype html>
  <html ${helmet.htmlAttributes.toString()}>
    <head>
      ${helmet.title.toString()}
      ${helmet.meta.toString()}
      ${helmet.link.toString()}
    </head>
    <body ${helmet.bodyAttributes.toString()}>
      <div id="content">
        ${body}
      </div>
    </body>
  </html>
`;

export const renderReactWithHelmet = (name, component) => hypernova({
  server() {
    return (props) => {
      const body = ReactDOMServer.renderToString(React.createElement(component, props));
      const helmet = Helmet.renderStatic();
      return template({ helmet, body });
    };
  },

  client() {
    const app = React.createElement(component);
    ReactDOM.hydrate(app, document.getElementById('content'));
  },
});

Note: if you're using Rails make sure to disable layouts since this renders the full html.

devcat22 commented 4 years ago

Couldn't you have done this right after the renderReact?