Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initial static React HTML rendering with Webpack

Is there a way to pre-generate the HTML structure of a (single route) React application directly in the HTML entry point?

Then the page will be able to display HTML (based on React initial state) before any JS is loaded.

I'm actually using webpack-html-loader but any other loader or plugin is welcome ;)

PS: May static-site-generator-webpack-plugin be of any help?

PS: I'm not using React Router

like image 903
Yves M. Avatar asked Feb 02 '17 20:02

Yves M.


1 Answers

If you want to use static-site-generator-webpack-plugin you first need to build a bundle with webpack bundle.js that exports a render function that takes following arguments.

  • locals an object with various page metadata e.g. title that go into component parameters (traditionally thought of as template variables).
  • callback a nodejs style (err, result) callback that you will call with your rendered html as the value for result

e.g.

// entry.js, compiled to bundle.js by webpack

module.exports = function render(locals, callback) {
  callback(null, 
      '<html>' + locals.greet + ' from ' + locals.path + '</html>');
};

It is in this function that you will instantiate your components (possibly via React Router if you want) and render them with ReactDOMServer.renderToString().

You will then specify the compiled bundle.js as bundle in your instantiation of StaticSiteGeneratorPlugin as well as your concrete routes in paths and in locals an object containing the above mentioned metadata values.

var paths, locals; // compute paths from metadata files or frontmatter

module.exports = {
  entry: {
    bundle: './entry.js' // build bundle.js from entry.js source
  },
  ...,
  plugins: [
    new StaticSiteGeneratorPlugin('bundle', paths, locals)
  ]
}

The keys you specify for locals in webpack.config.js in will be present in the locals parameter of every call to render(locals, callback). They will be merged with path, assets and webpackStats keys provided by the plugin.

If you want to load javascript code into your pages after rendering you could compile an additional page.js entry to your webpack config that calls ReactDOM.render() in the typical manner and then load that bundle in a script tag emitted by in your render(locals, callback) function in your bundle.js (above). Ensure that page.js mounts components to the same location in the DOM as they are when rendered by entry.js (you will probably set an id attribute on the parent element). You will also need to ensure that any location (i.e. route path) dependent variables align in both environments.

Check out the source code of Gatsby which also uses this plugin. You can also have a look at the source code for Phenomic for an alternative approach.

like image 124
Frank Wilson Avatar answered Sep 20 '22 14:09

Frank Wilson