Going Isomorphic with Express and React.js

Phil Parsons

Tagged: , ,

We’re underway with the development of our client side (front end) application with React.js after the last episode in this series covering the set up with Webpack. Now it’s time to create an application server with Express and structure the code to run as an isomorphic application. An isomorphic application is one where we share the application logic on both the client and the server. In this case that is the React.js components which will be rendered on the server using Express. The benefits of this reach beyond just code sharing as it helps us with Search Engine Optimisation, improving page load times and providing a richer user experience.

If you followed the first article in this series then you will already have Express installed. If not you should work through that article first or install it before continuing. Alternatively you can get up to speed by downloading the code for the last project. If doing this you will need to run npm install to get the dependencies installed.

We start by creating a new directory in the project folder named server. Add a new file inside the server directory named index.js with the following code.

Here we import express and call it to create a new application named app. Next we define a route for the application which is ‘/’. What this means is that when we visit the application without specifying a specific page or section the function we pass as the second parameter will be called to handle the request and provide an appropriate response, the text that is sent back to the client/browser. We use an arrow function that receives two arguments, the request object being received and the response object we will be sending back. For now we send back the message ‘Hello world from Express’.

Before we can run this file and test our server we must install the Babel CLI (the command line tools for Babel). We need to install these as we are writing the Node.js server code in the ES2015 syntax which, at the time of writing, is not fully supported in the most recent version of Node.js.

We install the babel-cli globally like we did with Webpack but the core babel library that the CLI tools will use was installed locally to our project in the last article. Once installed we need to create a configuration file for babel in ./.babelrc (note the file name starts with a dot). We do this to tell babel which presets we require to build our code and to ignore packages in the node_modules folder.

Run the server from the root of the project directory using the babel-node command.

This command will compile the ./server/index.js file using Babel and start the server. After a few seconds you should see the message ‘Server running’ in the terminal window. When you see this message visit http://localhost:3000 in your browser where you should be greeted with the message “Hello world f rom Express”.

We have a server running with very minimal effort thanks to Express! Kill the server now by hitting Ctrl-C in the terminal window where it is running.

Building React components for Express

Now that we have a server we need to look at how we will share our React.js components between the server and the client. There is going to be code outside of the components themselves that we won’t be sharing such as the code to fetch data as this will be handled differently in the two environments. What we do want to share is our React.js components and the data flow to update and render those components with Redux (we’ll be introducing Redux and RethinkDB in the next article).

Webpack enables us to build code for both the client and server environments. On the client we will build the entire application from the index.js entry point but for the server we will build the code in chunks from the top level (smart) components. We currently only have a single component ./client/components/app so lets set up Webpack to build this chunk for the server. Make the following changes to the existing webpack.config.js file.

Added here is a second set of configuration that targets node. This means that the code to be built is for use with Node.js. We define the entry point in the server configuration as an object which contains the names of the chunks we want to build. For now this just contains the app component. In the output section we specify that files should be saved in the directory ./server/generated and that each file be named according to the name given in the entry section using the ‘[name].js’ placeholder. We also specify that the output library type of the files should be commonjs2 the module system used in Node.js.

Running webpack now will show the build status for both sets of configuration. The client code is now saved into the file ./dist/bundle.js so you can delete the old ./bundle.js file from the previous tutorials if it exists. The server code is saved into the ./server/generated/app.js file ready for use in our Express server. We have an issue though, if we try to import and use the App component as it is now then we will receive an error. This error is due to the import of ‘./style.less’ which makes no sense to be importing in server side code. To deal with this issue we will extract the generated CSS into a separate file so that it can be included from our index.html page. To do this we install and configure the the extract-text-webpack-plugin to remove the output of the style-loader and save it into an external CSS file.

Run webpack again. We now have ./dist/bundle.css alongside ./dist/bundle.js and ./server/generated/app.css alongside ./server/generated/app.js with all CSS extracted from our JavaScript code for both environments.

Using React.js components on the server

Now we are able to import and use our App component in our Express application.

Start the express server again.

Open the page once again in the web browser at http://localhost:3000 and you should see the message “Hello world from a React component”. Congratulations! You just created the first part or an isomorphic application with Node.js. Now lets get the application back to the same state as it was at the end of the last article with the CSS and index.html page.

Adding templates to Express

Currently we are just sending the string rendered by the App component as the response to the request we receive from browser but we need to send our index.html file with the component string inserted within the main tag. To do this we are going to install and use a template library, in this case we will be using the Handlebars library for Express. Install and add it to the application now.

Edit ./server/index.js to register Handlebars as the view engine for our application. Doing this tells express how we want to render output for our responses.

We start by adding Handlebars as a template engine using the engine method on the app object before telling the app to use this engine as the view engine. In doing this we set the default layout to be main which is going to be the main parts of the index.html file created in the previous articles. Layouts are used to provide a HTML structure that is common to a number of pages. Create the directory ./server/views/layouts and move ./index.html to become ./server/views/layouts/main.handlebars. Edit main.handlebars so that it now looks like the below.

The {{{body}}} placeholder is where the contents of each view will be rendered. So far we only have a single view which is the result of rendering our App component but as the application grows we will add additional routes with additional views. Add the app view in the file ./server/views/app.handlebars and copy the below into it.

Save the file and run the application but this time run the application from inside of the server directory. Change the current directory before trying to run the server.

As before you should see the rendered text “Hello world from a React component” in the browser but there is a problem. If you look in the network tab of your browsers development tools you will see that both bundle.js and bundle.css return a 404 not found. This is because express is not yet configured to serve these static assets. Let’s configure express.

We tell express to use the static assets from the ./dist folder generated by Webpack and add some comments as our code is now starting to grow. At a later stage we will split the code out so that it is more easily understood and maintainable.

Stop the server if it is already running, ensure both bundle.js and bundle.css are built by running Webpack then restart the server. Opening the page in the browser now should show the rendered component with the styling applied.

If you’ve enjoyed the series so far why not sign up for free and receive updates about new article as we release them.

It's only fair to share...Tweet about this on TwitterShare on FacebookShare on Google+Buffer this pageShare on LinkedInPin on Pinterest

/ 9 Articles

Phil Parsons


  1. Tony

    Well, this certainly helped me out, I have a great starting point for my project, can’t wait to see what you have coming next.


    • Phil Parsons

      Great, thanks. I’m glad you are finding them useful.


  2. Ali Ayoub

    For anyone else currently receiving an error when attempting to bundle the initial App component for the server – the issue seems to be due to Babel. This re-write works:

    from: https://github.com/babel/babel/issues/2694

    Great article series by the way 🙂


    • Phil Parsons

      Thanks Ali, yes you are right about this issue and I will update the code structure in the next post due out this week. A more ES6 solution to this though would be to export the class beneath the declaration.


  3. Jim Wharton

    Upon trying to run babel-node, I get a nasty error:

    __source: {
    SyntaxError: Duplicate data property in object literal not allowed in strict mode
    at exports.runInThisContext (vm.js:73:16)
    at Module._compile (module.js:443:25)
    at loader (/usr/local/lib/node_modules/babel-cli/node_modules/babel-register/lib/node.js:128:5)
    at Object.require.extensions.(anonymous function) [as .js] (/usr/local/lib/node_modules/babel-cli/node_modules/babel-register/lib/node.js:138:7)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at /usr/local/lib/node_modules/babel-cli/lib/_babel-node.js:161:27
    at Object. (/usr/local/lib/node_modules/babel-cli/lib/_babel-node.js:162:7)
    at Module._compile (module.js:460:26)

    I’m wondering if there’s something messed up in babel-cli at the moment.


    • Phil Parsons

      Not sure what __source is in your server code but you could eliminate possible problems from external packages by ignoring node_modules in .babelrc. I should have added that anyway so will update the example now.


      • Jim Wharton

        Thanks for your reply. It turned out to be some nasty flux (no relation to the pattern) going on in babel-core. It appears to be handled after 6.1.4.

        Thanks for a fantastic tutorial.


  4. Markus Bergh

    “Once installed we need to create a configuration file for babel in ./.babelrc (not the file name starts with a dot).”

    I think you missed a letter here in the parentheses, “e”.


  5. Markus Bergh

    Great article once again!

    “Create the directory ./server/views/layouts and move ./index.html to become ./server/views/layouts/main.hanblebars.”

    Typo here with the file extension. 🙂


    • Phil Parsons

      Thanks, I’ve updated both typos.


  6. Johan

    Great stuff!

    I started out with Express recently but felt I needed some “pazazz” and thought about React (since everyone’s talking about it). Found some tuts. but none that was this thorough, concise and clear.

    You also used Handlebars which is great (although I used that with my Express app, the files I named index.hbs and not index.handlebars, go figure). I was glad to see how I could just implement React, with your help, into my Express app this way (the tutorial).

    10/10 to you and this article serie.

    Ps. Switched Less for Stylus which I prefer, all matter of taste.


Leave a Comment

Your email address will never be published or shared and required fields are marked with an asterisk (*).