Getting started with npm and Browserify in a React project

Tags:

This is the second article in a series where we build a Slack-style chat app with React.

So far, in the previous article, we’ve built a simple prototype. We want to start adding features to it – such as actually supporting multiple people chatting – but before we do that, let’s set up some tools.

npm and Browserify are useful tools which will make our lives easier in the long run as our app starts becoming more complex.

So, in this article, we’ll move our previous code to use npm and Browserify.

What are npm and Browserify?

npm is Node.js’ package repository / package manager. One of the big benefits of using npm in our project is being able to use the huge amount of libraries available on npm. It also makes it easy for others to collaborate in our project, as they can simply run npm install and get all the libraries our project uses.

npm can also help by allowing us to run scripts, such as build scripts or test scripts, without having to remember complex command line invocations. This is similar to Grunt or Gulp, but somewhat simpler.

Browserify allows us to use Node-style modules in the browser. Normally when we want to include some library code in our project, we include another script tag on the page. Managing the scripts and especially the correct order of scripts can become tedious, especially if we use a lot of small libraries as is popular with npm.

Browserify helps us solve this by letting us use CommonJS style require('filename') in our code. All of the required files are bundled together into a single file which we can then include on our page. As an added benefit, we can combine Browserify and Babel, so that we don’t need to include the Babel browser.js file on the page either.

Using require also makes it easier to tell which functionality our code is using, and it avoids relying on everything being available globally. If you’re familiar with ES6 module imports, it works in a similar way.

What about Webpack? You might’ve heard about a tool called Webpack, which is popular with React projects. Webpack has the same idea as browserify: It allows bundling your files together and using require(). However, Webpack is a bit more advanced than Browserify, and it has some features which are more useful for more complex apps. With the advanced featureset comes a price – Getting started with Webpack is not as simple as with Browserify.

This is why I’ve decided to use Browserify for now. It’s easier to get started with, and we don’t need any of the advanced Webpack features. Depending on where this project goes, we may discuss using Webpack in a future article. It’s worth noting that moving to Webpack will not require any JS code changes.

Installation

For these tools, we need to install Node.js. Almost all modern JavaScript tools use it, so if you’re not familiar with it yet, now is a good time to get started.

You can download and install Node by grabbing one of the packages from the Node.js website.

Once you’ve installed it, you should have the commands node and npm available on the command line. node runs the Node.js REPL, which allows you to run JavaScript code, similar to how you can run code in the browser’s console. This can be convenient for testing things, but what we’re interested in this time is the npm command.

Setting up npm

As we want to use npm to manage the libraries our code depends on, we first need a package.json file. This file is used by npm to track what libraries we need, and some other metadata.

First, create a directory for the project, and open a command line in the directory. We can easily create a package.json for our project using…

npm init

This prompts you to fill in some information about the project. If you publish your package on the npm website, the information is displayed there. If you don’t plan to publish the package, you can just hit enter to continue with the defaults – you can always change it later.

This generates a package.json file for us, which will look something like this:

{
  "name": "react-2",
  "version": "0.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Most of the fields should be fairly self-explanatory. We don’t need to modify the file by hand, but if you want to know what else you can put in it, check out this interactive package.json guide and npm’s package.json docs.

Installing packages with npm

Now that we have a package file set up, we can use npm to keep track of our dependencies.

Let’s install React and ReactDOM which we need for our app:

npm install --save react react-dom

Note: most npm commands should be ran in your project directory

The install command is used, surprisingly, to install packages from the npm repository. When we pass the --save flag to it, npm saves the installed package’s versions into our package.json file. This makes it possible to install all saved dependencies with just npm install with no parameters.

npm packages are installed in the node_modules/ directory. You usually don’t need to do anything with the directory yourself, but if you want to check the library source code, it’s available there. If you’re using Git, it might be a good idea to add node_modules/ to your ignore file so you don’t commit it into the repository. Instead, when cloning the repo, you simply use npm install which sets everything up.

Setting up a project directory structure

Next, let’s create a simple directory structure for our project. It doesn’t need to be anything fancy, as we can always change it later if necessary.

I’ll create two directories:

  • src/ – this is where our source code files will live
  • build/ – Browserify output goes here

The next step is to grab the code we had before, and change it a little bit so that it works with Browserify.

First, I’ll take the HTML we had before, and put it into index.html in the project dir.

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <div id="app"></div>
    <script src="build/app.js"></script>
  </body>
</html>

Note that this is much simpler than what we had before. We can remove all the libraries and the script tag in the body, instead just loading build/app.js.

Remember how I said Browserify bundles our files together? The build/app.js is this bundled file. We’ll look at how it gets generated in a bit.

We also need to modify the JavaScript code slightly. We had two components: Chat and ChatMessage, which you can see here.

Let’s put each component into its own file, so that we end up with the following…

src/ChatMessage.js

var React = require('react');
 
module.exports = React.createClass({
  render: function() {
    return <p>{this.props.message}</p>;
  }
});

The first change you’ll notice is the first line. Instead of using a global, we use a require to load React. Any packages you install via npm install can be loaded simply by saying require('name-of-package').

The second change is we are assigning the ChatMessage component into module.exports instead of a variable. Anything assigned into module.exports becomes available to other modules that require it. In this case, when we require the ChatMessage.js file, we would get the component. You’ll see this next, as we update the Chat component.

src/Chat.js

var React = require('react');
 
var ChatMessage = require('./ChatMessage');
 
module.exports = React.createClass({
  getInitialState: function() {
    return {
      text: '',
      messages: []
    };
  },
 
  submit: function(ev) {
    ev.preventDefault();
 
    var newMessage = <ChatMessage message={this.state.text} />;
 
    this.setState({
      messages: this.state.messages.concat([newMessage]),
      text: ''
    });
  },
 
  updateInput: function(ev) {
    this.setState({
      text: ev.target.value
    });
  },
 
  render: function() {
    return <div>
      <div>{this.state.messages}</div>
      <form onSubmit={this.submit}>
        <input onChange={this.updateInput} value={this.state.text} type="text" placeholder="Your message" />
        <input type="submit" value="Send" />
      </form>
    </div>;
  }
});

Like before, we need to load React using require. When using Browserify (or Node.js), each module is isolated. This means if we declare or require something in one file, it will only be available within that file. Because we need access to ChatMessage, we require it next.

Note that when we include code that is not installed via npm, we need to provide a path. In this case, since both Chat.js and ChatMessage.js are within the src/ directory, we can say require('./ChatMessage').

Remember that we put the ChatMessage component into module.exports. Thanks to that, now that we’ve required the file here, we can use the component. Similar to ChatMessage, we also assign the Chat component into module.exports so that we can load it from elsewhere.

Finally, we need one more script file.

Recall that we need to use ReactDOM.render to render the component into HTML. Let’s create one more script file, src/index.js

var ReactDOM = require('react-dom');
var React = require('react');
 
var Chat = require('./Chat');
 
ReactDOM.render(
  <Chat />,
  document.getElementById('app')
);

In this case, we need to load react-dom. Same as react, since we installed this from npm, we can simply use the name of the library when requiring it.

We also load Chat, for which we need the absolute path version, as it’s a file within our src/ directory.

But why do we load React, even though we’re not using it in this file? Remember that JSX gets compiled by Babel, and the output is React.createElement. If we don’t require react here, then we’ll run into an error later on.

Other than this, the code is same as before.

As a side note: The module.exports and require() style modules are known as CommonJS modules. Node.js, Browserify and Webpack all make use of this style. This is not to be confused with a different module style, RequireJS, which is not recommended to be used. An easy way to remember which is which is that it’s “upside down”: CommonJS uses require, RequireJS doesn’t.

Setting up Browserify

Now we’re in the final step of the process. In order to bundle our code together and make it work in a browser, we just need to run it through Browserify.

First, install Browserify:

npm install -g browserify

The -g flag makes npm install the package as global, meaning you get access to the browserify command from the command line. You may need to use sudo to run this.

And lastly, we need the babelify package, which allows browserify to use babel to compile JSX code.

npm install --save babelify babel-preset-react

With all the installations done, we can now bundle and compile the code.

browserify -t [ babelify --presets [ react ] ] src/index.js -o build/app.js

If you run this command, browserify grabs all necessary files, runs them through Babel, and bundles them into build/app.js.

What’s going on this line? -t flag specifies a transform – that is, we’re telling browserify to use babelify to transform our code. The additional parameters inside the square brackets tell babelify to use the react preset, which means it compiles JSX (and ES6) code into standard JavaScript.

The parameter after the brackets, src/index.js is the entry point. It’s the piece of code which browserify loads at first. Usually this file will just be the part of your code which does the rendering or other initialization logic.

The last parameter -o build/app.js tells browserify where we want the output. As we used build/app.js in our HTML, it makes sense to output the bundle there.

Since the command is a bit of a mouthful to type by hand, let’s make use of npm’s script utility. We can add custom scripts which npm helps us run by adding new properties into the scripts object in package.json:

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "browserify -t [ babelify --presets [ react ] ] src/index.js -o build/app.js"
  },

If you now run npm run build, the command we put into the scripts object is ran for us. Much easier to remember, don’t you think?

What’s next?

Now that we’ve got browserify set up, we can start making better use of other libraries. This also speeds up the page load times as we no longer compile our code on-the-fly.

I’ve created a Github repository for the project. You can find the code for this article in the v0.0.1 tag.

In the next article in the series, we do a bit of refactoring to improve the way we keep track of messages and state. Right now what we’re doing is a bit ad-hoc – we shouldn’t store application state within a component, but next time we’ll sort this out. Fixing it will also make it much easier to add support for multiple chat participants in the future!

Don’t miss any of the follow-up articles – sign up with the form below!