*This post was updated in April 2016 to reflect changes to Babel and the Webpack config

 

When I first started learning Facebook's React.JS library there were a couple things that threw me for a loop. First was the use of JSX, and its movement away from the .html, .css, .js separation of concerns that I knew and loved in exchange for a more component based separation. But like most React folks I quickly came around to this way of thinking. After all, very often our code was intrinsically tied together anyway, we were just putting it in separate files. The second thing that threw me off was a bit more unexpected, after going through a few initial tutorials I realized I didn't actually know how to get a React app online in a production environment. 

Coming from a background in Rails and similar frameworks, which often have a very well defined pipeline for deployment, I quickly found that React is completely agnostic about the matter. Not only that, but a few Google searches showed the number of different approaches that you could take was staggering. Much of this stems from the fact that React simply compiles to javascript, and there are so many different options out there to help you build, bundle and serve up your javascript assets. 

I eventually settled on a simple workflow using Express and Webpack to get my React apps online, with the wiggle room to add complexity over time if need be.

We're going to be adding / editing four files in the root directory of our project:

  • Our package.json file
  • Our Webpack configuration file, webpack.config.js
  • A simple Express server, that we'll name server.js
  • A procfile for Heroku

Let's start by setting up our package.json file. If you're new to things like Node and are coming from something like Rails, this is the rough equivalent of your gemfile, with a few extra bells and whistles. You can build this manually or by running npm init from the command line of your project directory.

Package.json

{
  "name": "simple-heroku",
  "version": "1.0.0",
  "description": "Our simple set up for React on Heroku",
  "main": "index.js",
  "repository": "",
  "scripts": {
    "start": "node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js",
    "prod": "NODE_ENV=production node server.js",
    "postinstall": "webpack -p"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "babel-core": "^6.2.1",
    "babel-loader": "^6.2.0",
    "babel-preset-es2015": "^6.1.18",
    "babel-preset-react": "^6.1.18",
    "webpack": "^1.12.9",
    "express": "^4.13.3",
    "react": "^0.14.3",
    "react-dom": "^0.14.3"
  }
}

A few different things are happening here. First off you may have noticed that we have three scripts, 'start', 'prod' and 'postinstall'. Each of these is short-handing some command line code. 'Start' will fetch and run our dev-server when we npm start, 'prod' will let us test our production build locally with npm prod by running our server.js file, and lastly the postinstall script will run automatically after our packages are installed, using webpack to build a bundle.js file that we'll get into in a minute. 

The second half of this file is where we list our project dependencies, in this simple example we're making use of webpack, express, react, and I've added babel to support ES6 syntax and give an example of how webpack loaders work. The versions are simply those available at the time of writing.

Speaking of Webpack.


Webpack.config.js

module.exports = {
  entry: [
    './src/index.js'
  ],
  output: {
    path: __dirname,
    publicPath: '/',
    filename: 'bundle.js'
  },
  module: {
    loaders: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader',
      query: {
        presets: ['es2015', 'react']
      }
    }]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  }
};

Webpack is a module bundler that is very versatile and works well with React. That being said it has a pretty steep learning curve. This is the section where you probably have the most leeway to use whichever tool works best for you, Browserify is another common choice, there are options to use things like Grunt and Gulp as well. We'll stick with Webpack here for now.

So what's going on is that messy looking file above? It's actually relatively straightforward. We're providing the path to the javascript file that serves as the entry point to our React application, in this case an index.js file, along with an output file name and directory (bundle.js building right to the root). 

The second thing that's happening, is we're running something called the babel-loader. This is a build step that will make use of Babel to convert our ES6 & JSX to browser-compatible ES5. We're making use of the two presets we installed babel-preset-2015 and babel-preset-react to get this done. There are a lot of different loaders and ways that you can chain them together to create the build process you are looking for, including things like CoffeeScript, SASS, and Jade but it's definitely beyond the scope of this post. You can check out more in the documentation

The last line is for a bit of convenience in our code, resolve allows us to shorten our files when using require. So we can require('fileName') vs require('fileName.jsx')


Server.js

var express = require('express');
var app = express();

app.use(express.static(__dirname + '/'));

app.listen(process.env.PORT || 8080);

This is a bare-bones Express server that we're putting together to serve up the static assets in our root directory. If you are building your app to a different directory, you can feel free to serve up that one instead. For Heroku purposes, the most important line of this file is the last one. We're telling our app that if you are being given the environment variable PORT, which is provided by Heroku, use it. If we don't have this variable then go ahead and use port 8080.


The final thing that we're going to need is a procfile for Heroku, telling it how to handle the web requests that come through. In our case we just need to run our server.js file and our set-up will take care of the rest, so this is a simple one-liner.

Procfile

web: node server.js

And that's it, now when we git push heroku master, our app should be online and good to go!

 

-Brad