These days server side rendering has become an important feature for heavy client side applications and now most of
the client side frameworks support it. Yesterday I tried a bit of react server
rendering along with express.js and react-router.
Setup Express js server and dependencies.
We can start with scaffolding a new express.js app using expressjs-generator and installing all the dependencies. We use webpack for client side
bundling.
We can install all the dependencies by running
and development dependencies by running
Setup webpack & babel for client.
We will keep all our client JavaScript and React components in a new folder named
client and put all the compiled js in public/javascripts. Also we shall add a
.babelrc to load babel presets and configs.
Now in webpack.config.js, we will configure the entry point, output directory and babel
loader.
Now we can run the following to build client JavaScript files in development mode with debug flag turned on, and watch for changes.
For easier use, we can add the command to npm scripts with name webpack:server. Now we just need to run npm run webpack:server.
Setup react router
Now the basic scaffolding and development setup is finished and time to start
building our app. We can start with configuring the router. We are planning mainly for
two routes /home to show the rendering of static component and /list to show
the server side rendering with some data.
First we have to define the entry point which will mount our react-router
component to DOM.
Next, define routes in client/router.jsx
AppRoot is nothing but a simple layout for our app.
Setup express js for server side rendering
Since we are using React + ES6 for components, we have to use the babel-register on server
side so that we can write express js routes also in ES6 and import the react routes we
already wrote. Please note that, we have to require/import the babel-register at the beginning of
express js entry point app.js.
Then we rename the routes/index.js to routes/index.jsx, after this we can use the
react routes and react components on server side. For server side rendering we use
renderToString method from react-dom/sever package and methods like match, createRoutes and RouterContext
from react-router.
match
function in react-router module will match a set of routes to a location and calls a callback, without rendering.
We use createRoutes
method from react-router to create a set of routes from our client/router.jsx(appRouter) component and provide it to match.
Once we have a match RouterContext will render the component tree for the given router state and return the component markup as a string with the help of renderToString method.
Now we have the react components rendered as string and we need to pass this to our pug.js (Previously known as jade) view.
The jade view will accept the string in content variable and substitute inside the react app mount point.
/home points to a static component called Home which we are going to render from server.
and now when we join the dots the routes/index.jsx will look like this
Rendering component on server with data
In this section, we are trying to render a list of users, the data source is not DB but an API for demo purpose.
In order to render data, we need to fetch data from the server, pass it to a component via context. For this we need to write a Higher Order Component to set the data to context.
The above DataProvider will set data to context if we pass it via props named data.
The List component will look like,
The above list component will look for data in context first, then in global state and later in component level state.
While we render it from server, the data will be available in context and component use the data in context to render the initial HTML.
Later after loading it in browser the component can fetch again and update the data.
Now we can setup the route to fetch the data and render the component.
Thats it. We successfully rendered our react components from server side with and without data, so that user don't have to wait
for another ajax request after loading the page to see the data.