Node.js & React Part 2

Austin Lyons
5 min readOct 11, 2015

--

Serving a React app from Node

In part 1 we installed Node and configured a very simple Node server. Now let’s serve up a basic React app from our Node server. In part 3, we’ll build a simplified JSONLint clone as an isomorphic/universal React app.

The source code can be found in this GitHub repo.

Serving HTML and JS from Node

Our Node server is currently just returning a string when it receives an HTTP GET request to the root directory.

// server.js
'use strict';
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('React app will go here');
});
const server = app.listen(9000, () => {
let port = server.address().port;
console.log(`Server running at http://localhost:${port}`);
});

Let’s update our server to return an HTML file. Eventually we’ll link to a JavaScript file that has our compiled React app. For now let’s have our HTML file just include a JavaScript file that prints “hi” to the console so we know that our wiring works.

We need a folder to put our JavaScript in. Let’s create a folder in the root directory that will house any static assets (ex: JavaScript, CSS, or images) called “public”. Within that folder, create a js folder to house the JavaScript with a file called “app.js”.

// app.js
console.log(‘hi from app’);

At this point we have a JavaScript file that will eventually house our React app. Now we just need our server to send an HTML file to the browser so it can execute our script.

Let’s create the following index.html file in our root directory.

<!DOCTYPE html>
<html>
<head>
<title>React app</title>
</head>
<body>Nothing yet</body>
<script src="/static/js/app.js"></script>
</html>

This HTML5 file will show the words “Nothing yet” when you load http://localhost:9000 and will also execute app.js which will print “hi from app” into your browser’s console.

To double check that your files are in the right place, if you install tree and run the following

tree -I 'node_modules'

You should see

.
├── index.html
├── package.json
├── public
│ └── js
│ └── app.js
└── server.js
2 directories, 4 files

Side note: We’re ignoring node_modules here, but you can run it once without the -I to see all the help we’re getting from other people (we’re using hundreds of JavaScript files already, for free!)

Now we’ll update our server. Let’s tell Node to make our static assets available via the ‘/static’ URL (i.e. localhost:9000/static) and to return index.html instead of a string for our root endpoint

// server.js
‘use strict’;
const express = require(‘express’);
const app = express();
app.use(‘/static’, express.static(‘public’));app.get(‘/’, (req, res) => {
res.sendFile(__dirname + ‘/index.html’);
});
const server = app.listen(9000, () => {
let port = server.address().port;
console.log(`Server running at http://localhost:${port}`);
});

Serving React from Node

Let’s serve up a React app now instead of a vanilla JavaScript file. First we will install React.

npm install --save react react-dom

Let’s use Webpack to bundle our React app. Webpack also has a cool development server also built using Node and Express that enables hot reloading (your app refreshes automatically when you change code), but let’s stick with our simple server right now.

npm install --save-dev webpack

We will be writing ES6 JavaScript, but we need to transpile it to ES5 as many browsers at the time of this writing don’t fully support ES6 yet. To accomplish this we’ll use Babel. Let’s install Babel plugins for Webpack.

npm install --save-dev babel-loader
npm install --save-dev babel-core

Edit: This post was written with Babel 5. As Marco pointed out in the comments below, for this to work on Babel 6 you need to also do:

npm install --save-dev babel-preset-es2015
npm install --save-dev babel-preset-react

Now we can finally play with React.

React

First, let’s add a div container in our HTML where we’ll inject the React app, and then we’ll update the script tag to include the React app we are about to make (react-app.js).

<!DOCTYPE html>
<html>
<head>
<title>React app</title>
</head>
<body>
<div id=”app”></div>
<script src=”/static/js/react-app.js”></script>
</body>
</html>

Now we’ll write some React. Let’s put our React components in a new top-level folder called “components” and add a new file called “ReactApp.jsx” for our first React component. The React component will simply return some text, and we’ll export that component so that other JavaScript functions can use it.

import React from ‘react’;let ReactApp = (props) => {
return (
<p>This is a React app!</p>
);
}
export default ReactApp;

Now let’s use that React component to render text and we’ll inject the text into the div container we added to index.html. Create a file called main.js in our top-level directory and add the following.

import React from ‘react’;
import ReactDOM from ‘react-dom’;
import ReactApp from ‘./components/ReactApp.jsx’;
ReactDOM.render(<ReactApp/>, document.getElementById('app'));

We’re almost done. Let’s tell webpack where to find our React app so it can transpile and bundle our files into a single JavaScript file.

// webpack.config.js for Babel 5var path = require(‘path’);
var webpack = require(‘webpack’);
module.exports = {
entry: './main.js',
output: { path: __dirname + '/public/js', filename: 'react-app.js' },
module: {
loaders: [
{ test: /.jsx?$/, loader: 'babel-loader', exclude: /node_modules/ }
]
},
};

Edit: If you’re using Babel 6, use the following webpack.config.js file

// webpack.config.js for Babel 6
var path = require(‘path’);
var webpack = require(‘webpack’);
module.exports = {
entry: ‘./main.js’,
output: { path: __dirname + ‘/public/js’, filename: ‘react-app.js’ },
module: {
loaders: [
{
test: /.jsx?$/,
loader: ‘babel’,
exclude: /node_modules/,
query: {presets: [‘react’, ‘es2015’]}
}]
},
};

Let’s run webpack now and hope for the best :)

>> webpack
Hash: dbc5aca6ee07af2a961b
Version: webpack 1.12.2
Time: 1350ms
Asset Size Chunks Chunk Names
react-app.js 675 kB 0 [emitted] main
+ 159 hidden modules

If everything worked, there should be a file called ‘react-app.js’ in your public/js folder.

>> tree -I ‘node_modules’
.
├── components
│ └── ReactApp.jsx
├── index.html
├── main.js
├── package.json
├── public
│ └── js
│ ├── app.js
│ └── react-app.js
├── server.js
└── webpack.config.js

Fire up our node server again, navigate to http://localhost:9000/ and now you should see “This is a React app!”

Next

We successfully served a React app from a Node server. But our server basically sends a blank webpage over on initial page load and we make the client (via our React app) inject the rest of the HTML into the page for us. If this was a real app, we might want to return more HTML from the server to help perceived load time and SEO. Next time we’ll write JavaScript on the server to send over HTML for us and then let the client take over to do the other dynamic things; this is called “universal Javascript” and a great explanation can be found here from StrongLoop and on this blog post from Netflix’s engineering team.

Click here to jump to part 3.

--

--

Austin Lyons

Product manager. Tech + strategy. Previous: software engineer + startups + published researcher at UIUC, small-town Iowan.