Getting Started with Browserify

Patrick Catanzariti

JavaScript implementations have been getting more and more complex as the beautiful beast we call the web evolves each year. Many of us now work with JavaScript modules – independently functioning components that come together to work as a cohesive whole, yet can happily have any component replaced without causing armageddon. Many of us have been using the AMD module pattern and RequireJS to accomplish this neatly.

Last year, Browserify hit the scene and brought on a lot of excitement. As the dust starts to settle, I wanted to write up an overview on what Browserify is, how it works, and a few options for adding it into your workflow.

What is Browserify?

Browserify allows us to use node.js style modules in the browser. We define dependencies and then Browserify bundles it all up into a single neat and tidy JavaScript file. You include your required JavaScript files using require('./yourfancyJSfile.js') statements and can also import publicly available modules from npm. It’s also quite simple for Browserify to generate source maps for you so that you can debug each JS file individually, despite the fact it’s all joined into one.

Why Import node Modules?

Importing modules is a blessing – rather than visit a range of sites to download libraries for your JavaScript, just include them using require() statements, ensure that the modules have been installed and you’re good to go. Commonly used JavaScript libraries like jQuery, Underscore, Backbone and even Angular (as an unofficial distribution) are all available to work with. If you’re working on a site that already runs node, you’re simplifying things even further with one common way to structure all of your JS. I really like that concept.

What You’ll Need

To get started with Browserify, the bare minimum you’ll need is:

  • node.js
  • npm – this comes installed with node by default.
  • Browserify – I’ll explain how to install this one.
  • A pack of JavaScript modules you’re ready to tame!

Getting Started

To get started, you’ll need node and npm installed on your computer. Head to the links above if you’re looking for guidance on getting these installed. If you’re totally stuck, try these instructions on installing Node.js via package manager. You won’t need to actually do any node work to use Browserify. We’re installing node solely because npm runs off it. Once you’ve got npm, you can install Browserify using the following command:

npm install -g browserify

What we’re doing here is using npm to install Browserify globally on your machine (the -g tells npm to install a module globally).

If you get an error that starts with the following:

Error: EACCES, mkdir '/usr/local/lib/node_modules/browserify'

Then you have a permission issue. You can sudo the command, but I recommend checking out this post instead.

Creating Your First Browserify File

Let’s start by creating a Browserified JavaScript file that imports an extremely popular module, Underscore. We’ll use Underscore to track down Superman. I’ve called my JS file main.js, and have placed it in a js folder in my project.

We start by assigning the _ variable to Underscore using Browserify’s require() statement in our JavaScript:

var _ = require('underscore');

Next, we’ll use the each() and find() functions from Underscore. We’ll search through two arrays of names and run a console.log to say whether it sees Superman or not. Highly advanced stuff Lex Luthor could only dream of. Our final JavaScript code will look like this:

var _ = require('underscore'),
  names = ['Bruce Wayne', 'Wally West', 'John Jones', 'Kyle Rayner', 'Arthur Curry', 'Clark Kent'],
  otherNames = ['Barry Allen', 'Hal Jordan', 'Kara Kent', 'Diana Prince', 'Ray Palmer', 'Oliver Queen'];

_.each([names, otherNames], function(nameGroup) {
  findSuperman(nameGroup);
});

function findSuperman(values) {
  _.find(values, function(name) {
    if (name === 'Clark Kent') {
      console.log('It\'s Superman!');
    } else {
      console.log('... No superman!');
    }
  });
}

We’ll want to ensure that Browserify can find the npm module when it tries to add it to our project. The bare basics of doing so involves opening up your terminal, navigating to the folder which holds your JavaScript project, and then running this command to install Underscore in that folder:

npm install underscore

For those unfamiliar with how node and npm work, this creates a folder called node_modules in your project which holds the code for your Underscore module. The command retrieves the latest version of Underscore from the npm repository at https://registry.npmjs.org/underscore. With that module in our node_modules folder, Browserify can now find it and use it.

Running Browserify for the First Time

When we run Browserify, it’ll want to build a new JavaScript file with all of our attached modules. In this case, it’ll build a JavaScript file with Underscore inside it. We’ll need to decide on a name for this new file, I’ve gone with findem.js. I run this command from my project’s root folder:

browserify js/main.js -o js/findem.js -d

This command reads your main.js file and outputs it into the findem.js file defined by the -o option. I’ve included the -d option so that it’ll generate a source map for us too, this way we can debug main.js and underscore cleanly as separate files.

Using the Browserify Output

From there, it’s as simple as including the file on your page like any other JS file:

<script src="js/findem.js"></script>

Importing Your Own JavaScript Files

It’s unlikely that all of your application will come from node modules. To include your own JavaScript, you can use the same require() function. The following line of JavaScript will import a JS file called your_module.js into the greatestModuleEver variable:

greatestModuleEver = require('./your_module.js');

To import our JavaScript like this, we just need to structure our JavaScript as a module. To do so, we must define module.exports. One way to do this is shown below.

module.exports = function(vars) {
  // Your code
}

Side Note!

If you’ve got a bunch of JavaScript libraries that aren’t in npm and you’re looking for an easier way to get these all into Browserify, you can use the Browserify-shim npm module to convert these files for you. We won’t be using it in this article but some devs might be keen to give that a go.

Our Example with a Module

To give a simple example of how this works, we’ll take out the arrays from the previous superhero search example and replace them with a separate JS module that returns an array of names. The module looks like so:

module.exports = function() {
  return ['Barry Allen', 'Hal Jordan', 'Kara Kent', 'Diana Prince', 'Ray Palmer', 'Oliver Queen', 'Bruce Wayne', 'Wally West', 'John Jones', 'Kyle Rayner', 'Arthur Curry', 'Clark Kent'];
}

Next, we’ll import that module into our code using names = require('./names.js'):

var _ = require('underscore'),
  names = require('./names.js');

findSuperman(names());

function findSuperman(values) {
  _.find(values, function(name) {
    if (name === 'Clark Kent') {
      console.log('It\'s Superman!');
    } else {
      console.log('... No superman!');
    }
  });
}

Our names variable references the exported function from our module. So we use the names variable above as a function with brackets when we pass in the array of names to our findSuperman() function.

Run that browserify command from your command line once again to compile it, open it in your browser, and it should run as expected, searching through each value in the array and logging whether it sees Superman or not:

Our console logs found Superman from our module

Passing in Variables and Sharing Modules Across Our App

To add a bit more complexity to this rather simple Superman hunting app, let’s turn our findSuperman() function into a module. That way, we could theoretically find Superman in various parts of our JavaScript and we could always replace our Superman hunting module with a more effective one in future quite easily.

We can pass in variables to our module and use them in our module.exports function, so we’ll create a module in a file called findsuperman.js which expects to be given an array of names:

module.exports = function (values) {
  var foundSuperman = false;

  _.find(values, function(name) {
    if (name === 'Clark Kent') {
      console.log('It\'s Superman!');
      foundSuperman = true;
    } else {
      console.log('... No superman!');
    }
  });

  return foundSuperman;
}

I’ve added a return value for our findSuperman() function. If it finds Superman, it’ll return true. Otherwise, it’ll return false. It’s up to the code that uses this module to decide what it uses this true/false value for. However, there’s one thing we’re missing in the module above. We’re using Underscore in our function, but haven’t declared it. We can declare it in the module itself as well at the top like so:

var _ = require('underscore');

module.exports = function (values) {
  ...

When using Browserify, it’ll look through all of your JS files that are imported and will only import each module that is mentioned once. So we’re requiring underscore in our main JS file and we’re requiring it in findsuperman.js but when Browserify packages it all up, it only puts it in our final JS file once. Pretty neat right?

Our actual JavaScript app will now use our new module with its new returned true/false value. For demo purposes, we’ll just stick to a simple document.write to say whether or not it found Superman from our names:

var _ = require('underscore'),
  names = require('./names.js'),
  findSuperman = require('./findsuperman.js');

if (findSuperman(names())) {
  document.write('We found Superman');
} else {
  document.write('No Superman...');
}

We don’t even need to import Underscore in our main JS file anymore, so you could remove it without any drama. It’ll still get imported in the end through its inclusion in the findsuperman.js file.

Managing Browserify’s npm Dependencies with package.json

Say you have a keen friend who’d like to also use your code. It’d be a bit tough to expect them to know they need to install the npm underscore module first. The solution to this is creating a file called package.json in the root of your project. This file gives your project a name (make sure there are no spaces in the name here), description, author, version and most importantly in our case – a list of npm dependencies. For those who’ve developed with node, we’re using the exact same stuff here:

{
  "name": "FindSuperman",
  "version": "0.0.1",
  "author": "Patrick Catanzariti",
  "description": "Code designed to find the elusive red blue blur",
  "dependencies": {
    "underscore": "1.6.x"
  },
  "devDependencies": {
    "browserify": "latest"
  }
}

The list of dependencies is currently limited to our single "underscore": "1.6.x", where the first part of the dependency is the name and the second part is the version. latest or * will retrieve the latest version npm has. Alternatively, you can put in numbers such as 1.6 (for version 1.6) and 1.6.x (for versions 1.6.0 up to but not including 1.7).

We can also include browserify itself as a dependency, however it isn’t a dependency for the project to run – any user to our app can find Superman without needing to run Browserify. It is one of our devDependencies – modules required for developers to make updates to this app.

Now we’ve got a package.json file, we don’t need to get our friend to run npm install underscore. They can just run npm install and all necessary dependencies will be installed into their node_modules folder.

Automating the Browserify Process

Running browserify in the command line every single time you change the file is annoying and not at all convenient. Luckily there are a few options available to automate the running of Browserify.

npm

npm itself is able to run command line scripts just like the ones you’ve been typing in manually. To do so, just place a scripts section into your package.json like so:

"scripts": {
  "build-js": "browserify js/main.js > js/findem.js"
}

To run that, you can type the following in your command line:

npm run build-js

But that’s not convenient enough. We’ve still got to run that command manually each time. That’s annoying. So a better option is to use an npm module called watchify. Watchify is simple, it’s easy and it’s a huge time saver. It’ll watch for changes to your JS and automagically re-run Browserify.

To get this into our package.json, we’ll add it to our devDependencies and include a new script for watching our JS (leave build-js there for times when we do want to build our JS without needing to change the file).

"devDependencies": {
  "browserify": "latest",
  "watchify": "latest"
},
"scripts": {
  "build-js": "browserify js/main.js > js/findem.js",
  "watch-js": "watchify js/main.js -o js/findem.js"
}

To run this, just type in the following command.

npm run watch-js

It will run and work its magic. It doesn’t say much though to let you know what’s going on, which can be confusing. If you’d prefer it to give you details on what it’s doing, add -v to your watchify command like so:

"watch-js": "watchify js/main.js -o js/findem.js -v"

That’ll give you feedback like this each time it runs:

121104 bytes written to js/findem.js (0.26 seconds)
121119 bytes written to js/findem.js (0.03 seconds)

Generating Source Maps in npm

To generate source maps using npm, add -d after your browserify or watchify command:

"scripts": {
  "build-js": "browserify js/main.js > js/findem.js -d",
  "watch-js": "watchify js/main.js -o js/findem.js -d"
}

To have both the -d for debugging and -v for verbose output in watchify you can combine them like so:

"watch-js": "watchify js/main.js -o js/findem.js -dv"

Grunt

A lot of people (myself included) have been using Grunt for a while now and are pretty used to it. Luckily, for those sorts, Browserify plays nicely with Grunt builds too!

We’ll need to change our package.json file in order to use grunt. We won’t be using the scripts section anymore, and instead will be relying on Grunt for that. Instead we’ll add a few new devDependencies:

{
  "name": "FindSuperman",
  "version": "0.0.1",
  "author": "Patrick Catanzariti",
  "description": "Code designed to find the elusive red blue blur",
  "dependencies": {
    "underscore": "1.6.x"
  },
  "devDependencies": {
    "browserify": "latest",
    "grunt": "~0.4.0",
    "grunt-browserify": "latest",
    "grunt-contrib-watch": "latest"
  }
}

We’ve added to our dependencies:

  • grunt – to ensure we’ve got Grunt installed for the project.
  • grunt-browserify – the module that’ll allow you to run Browserify inside Grunt.
  • grunt-contrib-watch – the module that’ll watch our files and run Browserify any time they change.

We then create a file called gruntFile.js in the root of our project. Inside this Grunt file we’ll have the following:

module.exports = function(grunt) {
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-browserify');

  grunt.registerTask('default', ['browserify', 'watch']);

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    browserify: {
      main: {
        src: 'js/main.js',
        dest: 'js/findem.js'
      }
    },
    watch: {
      files: 'js/*',
      tasks: ['default']
    }
  });
}

We start in our Grunt file by loading the npm modules that we required in our package.json file:

grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-browserify');

We register our one and only group of tasks we’d like to run as our default task (browserify and watch):

grunt.registerTask('default', ['browserify', 'watch']);

We set up our Grunt initConfig object (all Grunt files look for this):

grunt.initConfig({

Within that, we point out where our package.json file is:

pkg: grunt.file.readJSON('package.json'),

Our Browserify settings are next and they basically set up where our source JS file is with our Browserified code and the file we’d like that to be built to:

browserify: {
  main: {
    src: 'js/main.js',
    dest: 'js/findem.js'
  }
},

We then set up a watch task to rerun our Browserify task whenever anything changes in the js folder:

watch: {
    files: 'js/*',
    tasks: ['default']
}

Because of our new devDependencies (we don’t have Grunt installed in our project, nor do we have either of those modules), we’ll need to npm install first. Once you’ve allowed it to run and install any modules, you can then run the ever so simple gruntcommand to get it to start watching your project.

Generating Source Maps in Grunt

With version 2.0.1 of grunt-browserify, the way that source maps need to be defined changed causing a lot of guides online to be incorrect! The correct way to get Grunt and Browserify to generate source maps for you is to add debug: true inside bundleOptions within options like so:

browserify: {
  main: {
    options: {
      bundleOptions: {
        debug: true
      }
    },
    src: 'js/main.js',
    dest: 'js/findem.js'
  }
},

That complicated looking options set up is intended to allow for the inclusion of future Browserify options in a nice and easily compatible way.

Gulp

Gulp is Browserify’s tabloid lover. Articles around the web pair the two quite often, Browserify and Gulp – the cutting edge JavaScript build process made in heaven. I wouldn’t say Browserify fans need to use Gulp, it’s mostly a personal preference between the different syntaxes. You can (as you’ve seen above) quite happily use npm or Grunt to build your Browserify file. I’m personally a fan of the clean and simple npm build process for smaller projects.

To do the above in Gulp, we’ll start by installing Gulp globally:

npm install -g gulp

We’ll update our package.json file to include a few new devDependencies we’ll need:

"devDependencies": {
  "browserify": "latest",
  "watchify": "latest",
  "gulp": "3.7.0",
  "vinyl-source-stream": "latest"
}

We’ve added the following:

  • watchify – we used this above in the npm example too. Same module.
  • gulp – the rather important module to give us all that Gulp goodness!
  • vinyl-source-stream – this is the module that’ll take an input and return a file for us to put somewhere.

Browserify has a streaming API for its output that we can use directly in Gulp. A bunch of guides will suggest using the gulp-browserify plugin, however Browserify do not recommend this and prefer us using Browserify’s streaming API output. We use vinyl-source-stream to pick up this Browserify output and place it into a file for us to output somewhere.

We then create a file called gulpfile.js in the root of our project. This is where all the Gulp functionality will go:

var browserify = require('browserify'),
    watchify = require('watchify'),
    gulp = require('gulp'),
    source = require('vinyl-source-stream'),
    sourceFile = './js/main.js',
    destFolder = './js/',
    destFile = 'findem.js';

gulp.task('browserify', function() {
  return browserify(sourceFile)
  .bundle()
  .pipe(source(destFile))
  .pipe(gulp.dest(destFolder));
});

gulp.task('watch', function() {
  var bundler = watchify(sourceFile);
  bundler.on('update', rebundle);

  function rebundle() {
    return bundler.bundle()
      .pipe(source(destFile))
      .pipe(gulp.dest(destFolder));
  }

  return rebundle();
});

gulp.task('default', ['browserify', 'watch']);

We start by importing in our npm modules which is fairly self explanatory. We then set three variables for our build:

  • sourceFile – the location and filename of our Browserified file (in this case js/main.js)
  • destFolder – the folder location we are outputting the final file to
  • destFile – the filename we want our final file to have

I’ll explain the code in a bit more detail below.

How Browserify and Gulp work together

Our first task is our browserify one which we define like so:

gulp.task('browserify', function() {

It first passes our main.js file into the Browserify npm module:

return browserify(sourceFile)

We then use the Browserify streaming API to return a readable stream with our JavaScript content:

.bundle()

From there, we pipe it into a file with the filename findem.js and then pipe that through to Gulp to put into our js folder.

.pipe(source(destFile))
.pipe(gulp.dest(destFolder));

We’re basically taking our input through various stages that eventuates into our final project which should be a shiny new JavaScript file!

Combining Watchify and Gulp

As learnt previously, it’s a little annoying to use Browserify directly as it’s much easier to have it run automatically when you update the file. To do this, we use the watchify npm module again.

We start by setting up a task called watch (you could call it watchify if you’d like… it’s really up to you here):

gulp.task('watch', function() {

We assign the watchify module to the bundler variable as we’ll use it twice:

var bundler = watchify(sourceFile);

We then add an event handler which runs a function called rebundle() anytime the update event is called. Basically, whenever watchify sees the file change, it’ll run rebundle():

bundler.on('update', rebundle);

So what is rebundle()? It’s pretty much exactly what our browserify task was doing above:

  function rebundle() {
    return bundler.bundle()
      .pipe(source(destFile))
      .pipe(gulp.dest(destFolder));
  }

  return rebundle();
});

It’d be possible to merge both browserify and watchify together in some keen JavaScript optimisation but I decided to leave them separately in this article to keep things simple. For an impressive and more complex example of this, check out Dan Tello’s starter Gulp file.

To finish our gulpfile.js, we define our default task which works the same way as the default task in grunt.

gulp.task('default', ['browserify', 'watch']);

To run the above Gulp code, you’ve got three options. The easiest way is to run that default task you made, which requires only one word on the command line:

gulp

That’ll run the browserify task once and the watch task will begin watching the files for any changes.

You can also specifically run your browserify task:

gulp browserify

Or your watch task:

gulp watch

Generating Source Maps Using Gulp and Browserify

To generate a source map for your JavaScript, include {debug:true} in both bundle() functions.

Our browserify task would look like so:

gulp.task('browserify', function() {
  return browserify(sourceFile)
  .bundle({debug:true})
  .pipe(source(destFile))
  .pipe(gulp.dest(destFolder));
});

The rebundle() function in our watch task would look like so:

function rebundle() {
  return bundler.bundle({debug:true})
      .pipe(source(destFile))
      .pipe(gulp.dest(destFolder));
}

Conclusion

It’s still quite early days for Browserify and it will surely evolve and mature as time progresses. In its current state, it is already a very handy tool for structuring your modular JavaScript and is especially brilliant for those who are using Node on their backend. Code becomes much cleaner for Node developers when using npm modules in both the front and back end of a project. If you haven’t given Browserify a shot, try it in your next JavaScript project and see if it rocks your world.

Other Resources

There are a ton of other Browserify resources out there. A few handy bits and pieces you might want to check out:

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • jokeyrhyme

    Is there a way to use avoid the build step entirely during development and only use Browserify at the end?

    One of the reasons I’m still using Require.JS and AMD modules is that the build step is completely optional, and something I only need to do prior to deployment.

    • bfredit

      Considering that CommonJS modules need to load synchronously I doubt it, but if you use watchify the build should be pretty fast.

    • Patrick Catanzariti

      I believe that you will need to always use the build step at the moment, but it’s quite simple to have running in the background and doesn’t cause too much delay. As bfredit mentioned – watchify is pretty fast. I’m curious though, is there a particular reason you’re looking to avoid the build step?

      • jokeyrhyme

        Primarily speed. I do most of my development with Vagrant boxes, or Docker (which uses VirtualBox on OS X). VMs are always slower than the host, especially when it comes to manipulating files.

        • Patrick Catanzariti

          Ah I see, that’s a fair point! If you do give Browserify a go on your VMs, I’d love to know how fast it compiles. If you give Watchify a go, please post back your findings on speed :)

  • bfredit

    watchify makes partial builds, so it’s much faster than building the whole bundle every time—much faster. My builds take 10 seconds or more, with watchify they take less than one

    • Patrick Catanzariti

      Thanks bfredit for clarifying :) I second your response, watchify speeds things up quite a bit by focusing in on just the Browserify stuff.