Quick Start Tutorial: Universal React, with Server Side Rendering
Fortunately, React provides a way to avoid these issues: rendering on the server.
You can use this method to generate HTML on the server and send the markup down on the initial request for faster page loads and to allow search engines to crawl your pages for SEO purposes.
Perfect. To render React on the server, our example app will handle requests using routes, and respond with markup generated using React components and the app state. When the app loads in the browser, we will use the same routes, components, and state to initialize React, handing off rendering from the server to the browser.
Back to the Comment Box
The official React tutorial demonstrates how to build a “simple but realistic comments box.” In my previous article, I modified the Comment Box code to demonstrate Redux usage: Quick Start Tutorial: React Redux. If you are new to React, I recommending checking out those tutorials first.
This tutorial for Universal React builds on the Comment Box, with Redux already integrated, to demonstrate Server-Side Rendering and React Router. The code for this revised example app is on Github.
- react, react-dom: to create and render React components on the server.
- redux, react-redux: to manage the state of application data.
- react-router: to demonstrate React Router usage.
- marked: since the original Comment Box tutorial uses Markdown to format text in the browser, we need the same library to format text on the server.
I added a polyfill (polyfill.js) that ensures browser support for Object.assign, a method used by our Redux reducer.
I also wrote a require-shims.js file which intercepts node.js require and module.exports in the app when node modules are loaded in the browser. This is somewhat absurd, and is only a stop-gap to let you check out and run the example app without having to install webpack just yet. More about webpack later in this tutorial.
The example app uses the Express web framework. Routing in the app is primarily handled by Express, with just two routes passed on to React Router: the default route “/”, and “/another-page”:
A nice aspect of React Router is that it’s declarative: routes express the way our components are structured. The Index component, which renders basic layout, wraps around two child components, CommentBox and AnotherPage.
In the webpage, we link to these URLs, “/” and “/another-page”, using the React Router Link component, which lets users transition instantly between pages in the browser:
You can learn more at the React Router docs.
As our routes attest, we are rendering React components called Index, CommentBox, and AnotherPage. Index (index.js) is the index.html from the original tutorial converted into a React component. CommentBox (commentbox.js) and its child components are used for displaying and adding comments. AnotherPage is a simple example component, defined right in routes.js for convenience.
Index and CommentBox, while similar to the original Comment Box app, have been modified to use Redux. Check out my previous tutorial to understand the code changes in CommentBox, and the reducer I extracted to redux-store.js.
This configureStore function calls Redux’s createStore with our reducer and initial state.
In the webpage, this state is converted to a JSON string, with script tags escaped for security, and then embedded in a HTML script tag as a variable called window.__INITIAL_STATE__:
The window.__INITIAL_STATE__ variable then populates the Redux store in the browser. Yeah, it’s a bit circular, but relatively straightforward considering we’re creating a webpage that is propelled into reconstructing itself, like a slinky on a treadmill.
Initial Render: Server and Client
Now that our routes, components and state are integrated, we’re ready to go.
First, on the server we call ReactDOMServer.renderToString and use Express to send the generated markup as a response:
In the browser, we parallel this using ReactDOM.render:
And we’re all set! Call ReactDOMServer.render and ReactDOM.render with the same components and initial state, and React will recognize that components don’t need to be re-rendered in the browser.
If you call ReactDOM.render() on a node that already has this server-rendered markup, React will preserve it and only attach event handlers, allowing you to have a very performant first-load experience.
And in the response, instead of only sending JSON, we use Express’ req.accepts to determine the kind of response to send. Ajax requests receive a JSON response, while regular HTML form submissions get redirected back to the Comments page.
As mentioned earlier, require-shims.js is a brittle workaround to enable you to run the example app without webpack. However, as you make your own apps, you will need to use a bundling tool at some point, both to get rid of all the script tags and replace it with one bundle, and more importantly, to pre-compile JSX code because compiling it in the browser is slow.
To start using webpack in the example app, first edit index.js. You’ll see a block of scripts to comment out or delete when using webpack, and one script tag to include. Go ahead and remove the script tags in the upper block, and uncomment the script tag for the webpack bundle:
Then install webpack and babel-loader.
The repository already comes with a webpack.config.js that defines some configuration for webpack. All you’ll need to do is run it:
More about Webpack at webpack-howto.
Now you have a sense of how to render React on the server. It can be intricate to put the moving parts together, but as long as you remember to run the initial render on the server, before rendering the same components with the same state again in the browser, you should be on the right track. As DJ Khaled says: “You smart.”
If you found this article valuable, Paypal tips are appreciated.
- Discussion on Hacker News.
React Comment box tutorial
- React Comment Box tutorial: Tutorial, Github
- Redux-enabled Comment Box: Tutorial, Github
- Server-Side Rendered Comment Box: Github
- react-server-example: A simple example of how to do server-side rendering with React.
- Tutorial: Handcrafting an Isomorphic Redux Application (With Love)
- React Router documentation
On Server-Side Rendering
- 7 Principles of Rich Web Applications
Questions or feedback? Let me know in the comments.