Building TDD React boilerplate with ES6

Part 1. Introduction to Modern Frontend Workflow, adding our first React Component.

In this part we’ll create our first React component, set our Front-end development environment with Node.js, create gulp build tasks, and will make sure things are running with latest JavaScript - ECMAScript 2015 (aka ES6). You can browse Github repository at the point of history for Part 1.

Why TDD React boilerplate and why just don’t start with plenty of manuals already available online? Two reasons. First, React has been updated from 0.13 to 0.14. Most of examples I found were outdated already. When it comes to TDD… this decision it’s up to you. I’ve seen many mature developers who don’t use TDD approach with React just because it seems that front-end related stuff is not easy today.

There are few ways to test React apps. What I really want to cover here is React unit testing. It’s relatively easy to cover your React app with black box tests, but unit testing is something different. You need environment for that, you should be already familiar with a couple of tools and technologies to pick the best. And it took me about 2 days to build my own boilerplate that just works.

In 1-2 weeks by doing my React job and researching the subject I found that my boilerplate code can be improved more. I thought it can be really useful for all engineers who want to use React to read about my experience.

Let’s start from basics.

Installing Node.js

Yes, you need Node.js to be installed and configured before we can even start. And here is the first stumbling block. I head that many times (especially from Ruby devs): “But we are Rails developers, and Node.js is completely different technology, why do we need it?”. Well, first of all - this is what we have now, you will need it. If you’re unlucky and unfamiliar with basics of Node.js - you have to do it. Get ready to learn something new. There is no way to avoid that. But good news here is that you don’t need to combine (it terms of coupling) two two technologies for the same project. You’ll be good your with Rails or other framework, and you won’t pollute development environment too much with bunch of new and sexy Node.js technologies. We’ll talk about that later in “Using Bower” section.

Everything in this article is tested on Mac OS X. You shouldn’t have issues configuring stuff for Linux. But when it comes to Windows - nobody knows. It can be pain I think. Moreover, if you’re using Vagrant and Windows it can be pain. Node.js modules can be nested deeply in your directory structure and Windows host machine can stuck here. I had few errors related to that. There is 95% chance that you’ll face issues with running Node with Vagrant+Windows. What you can do here is just to configure your Node application to be run somewhere else on your filesystem (not in shared directory like /home/vagrant).

If you have something to share, please share your thoughts/experience in comments. If you can provide a link to demo repository - please do.

And let’s finally start. We’ll start with major philosophical issue. Before you even install Node.js you have to decide how are you going to install global packages. There are two ways:

  • Standard way: sudo npm install something -g (-g here means globally. You can also do npm i instead of npm install)
  • The way I prefer: npm install something -g (without sudo).

I highly recommend to read a little bit about this issue and install Node.js with this manual (see “Solution” section). Let’s check if it works:

I have 4.x version of Node installed, you may have version 5.x - it doesn’t matter too much for now. You have also just installed gulp. Some people prefer grunt, which is little bit more mature, but personally I prefer to configure things with code rather than using metadata - it’s the main difference between gulp and grunt. With grunt you have to you json configuration. But we’re already too far, let’s initialize our project with Node.js.

Creating new project with Node.js

Create and switch to directory:

Then type command to create project.json. I’ll use default answers:

Congratulations, your project.json is ready! This is the main file where we’ll keep all dependencies, configuration, test commands.

More prerequisites

We’re about to add new modules to our project. But before let’s make sure we’re good with few more prerequisites. You’ll need:

  • Essential build tools (XCode on Mac, sudo apt-get install build-essential on Linux, Visual Studio Redistributable C++ pain on Windows).
  • Python 2.7.x - should be preinstalled on Mac OS X, type python -v to check.

Adding few dependencies

gulp is absolutely required for us. We won’t be able to run anything without it. And we must add it as dependency. Adding dependency means that other developers won’t need to install everything globally. And will be able to use our packages as dependency locally. Locally means that these packages will be installed to directory ./node_modules right in your application directory when you type npm install.

Let’s do it:

With --save-dev option npm will add gulp to your development dependencies section inside of your project.json. You can add as many modules as you want, and folks from your team will be able to install them with just one command (npm install).

Now you’ll have node_modules directory. It’s probably a good time to add it to ./gitignore:

At this point you’ll have another file npm-debug.log. It’s something to keep for your own debugging purposes, and I don’t recommend to to include it in your repository, so let’s ignore it too:

Start and test commands

For our purposes we’ll add two commands: start and test. We should be able to use:

  • npm start to kick off our development server locally, for development purposes
  • npm test to run unit tests

We’ll get back to npm test command in the next sections, let’s add start command now. Update your package.json with the following code in scripts section:

Don’t forget to add comma after "test"..., you package.json should look like this now:

Let’s type check if it works:

Our command works, and now it’s a good time to add so-called gulpfile. What is this? Basically, it’s JavaScript program. Nothing fancy, just the way to tell gulp what to do. Inside of gulpfile we usually have commands for our purposes. What kind of purposes we may have? It could be something like this:

  • Start development server
  • Copy files from one directory to another
  • Convert less or sass files to css
  • Concatenate files, etc.
  • Minify JavaScript files
  • Convert CoffeeScript to JavaScript

Gulp is very useful. Let’s say you have jQuery plugin written with CoffeeScript. You want to release it with JavaScript in two files: minified and not minified, and you want to keep the source. You’ll have two directories: src for source code, and dist for distribution package. You need one single command to convert from CoffeeScript to JavaScript, to copy files, to minify files, etc. You can look at my very simple jQuery plugin repository to see how it works, and if you want to learn more about Gulp (note that gulpfile is written with CoffeeScript - I tend not to use it now and prefer ES6).

Adding Gulpfile

The very first thing I would like to do is to create command for starting the server. It should just show us the very basic index.html:

Move this file to src directory for now. We’ll configure our development server for src directory, which is not good. You probably want to keep src directory for sources only. On the next steps, we’ll reconfigure development server to dist directory.

Let’s create our first gulpfile.js. If you’re not familiar with gulp you may want to read “Getting started with Gulp” online. I won’t cover gulp basics here.

Note that the code above is not ES6 yet. We’ve created the task with the name server. And in package.json we already have this:

…so if we type:

…you’ll have gulp’s server task executed. It should give you an error:

Yes, module gulp-server-livereload can’t be found. We need to install it and add to our package.json as dependency:

Run npm start again and… Congratulations! It works on http://localhost:8000/:

78f33042-d974-41dc-9810-f776f9fe3b54

Here is my directory structure:

2acd85be-ed6d-435d-927e-a1f7d9e62d52

I use Git and Atom editor, and green files above are new ones, yellow - modified since last commit, light gray - unmodified, dark gray - git-ignored.

Output from npm start:

Browse repository at this point of history

Adding very basic React component

Let’s add very basic React component. Oops, we’ve just violated fundamental TDD principle - tests first. But here is the wisdom:

tdd or not tdd? My answer: if you already know how to do it, tdd! If you don’t know - write the fucking code first.

Believe me, it’s not hard to start with TDD right now. But for the sake of your own understanding I don’t do it now. We need to configure a lot before we can even write our first test. So let’s configure ES6 and React, and will get back to adding a test later.

I’ll probably start with adding few more gulp tasks, so you won’t be overwhelmed with a bunch of new stuff later. I’ll reconfigure our development server to be serve files from ./dist folder. For this we need to copy all of our html files from ./src to ./dist directory. Here is the gulp task for that:

Running gulp copy-html will give you this output and create dist directory if it doesn’t exist:

And now it’s up to you if you want to add dist directory to your .gitignore. ./dist will be something ready to use for us. Moments later we’ll move our compiled version of React components there. And if you have .less or .sass - their compiled .css files will be there as well. For this boilerplate I’ll add ./dist to my .gitignore.

To make development server work we need to reconfigure it in our gulpfile.js:

Well done, now we’re serving files from ./dist, but I have two caveats for you:

  • If we change something in ./src directory, it won’t be live-reloaded anymore. We’re serving files from ./dist now.
  • You have to type gulp copy-html now before you type gulp server (or npm start). You have to copy files to ./dist folder now.

Using two commands are not very convenient. It’s really easy to miss gulp copy-html. We need to automate it more. Let’s tell gulp: “every time I run gulp server, execute copy-html task”. Fortunately, it’s really easy to do. We can inject dependency to our server task. You just need to provide array of tasks you want to be executed before your command:

It’s super-cool, and no need to execute gulp copy-html anymore. But wait, I have another warning for you. Command above will work well, until you have something like this:

JavaScript has asynchronous nature. Everything in array will be executed asynchronously. Basically, for us this line means: “Before running server command run a bunch of other commands, don’t wait for them to finish, just hope they’ll be finished before our development server will serve the output of these commands”. Side-effect is very interesting, and remains intermittent until your project grows really big: you will just notice that sometimes your React components generate errors on first load (or live reload), but never do it on the second reload. Just know that it’s because gulp execute these commands asynchronously.

I this problem with sequence module this way:

I think I won’t cover gulp tricks too much in this manual. Basically, the line above says: “execute clean command, then execute array of commands asynchronously, then execute final callback to tell gulp everything’s finished). We’ll add the sequence-thing in the next section.

Now our gulpfile.js looks like this:

Output from gulp server command:

Note that task copy-html is executed automatically when we type gulp server. It’s done. But we have another issue: when you modify files in ./src directory, you won’t have your server live reloaded, because the server is watching for changes in ./dist directory. To solve this we just need to do the following: when files are modified in ./src directory, build them and copy to ./dist.

More Gulp magic

Let’s agree on what are we going to have before doing any magic. We need the following:

  • Before build, clean ./dist directory.
  • Build everything, which means: copy static html files, build React components (compile a bunch of .jsx files to one JavaScript file bundle.js) and copy them to ./dist.
  • Watch for changes in ./src (!) folder. When changes are detected, just rebuild everything. Our development server is already configured to listen for changes in ./dist and will reload the browser automatically. When we rebuild everything, everything gets copied to ./dist.

Cleaning build directory

Can be done with gulp-rimraf. Clean task itself:

And install the module:

Execute clean command:

Now we can clean ./dist folder with one command.

Adding a sequence to build

Let’s think about the build process. What does it mean for us? If you come from C/C++ it can mean compile and link. If you came from Ruby it can mean nothing, because your programs are ready to use when you write them. But for us it means:

  • Clean and wait until it’s done (don’t forget about asynchronous nature of JavaScript)
  • Copy static html, convert React .jsx to .js (can be done asynchronously)
  • Wait for all tasks to be done, because we rely on results of build process

JavaScript representation of sequence above will look like this:

With using run-sequence it will look like this:

And gulp task will look like this:

Now our build task depends on clean (it should be finished first), copy-html, and browserify. Don’t forget to install run-sequence:

In your gulpfile.js you can place tasks at any place you want, in any order. But personally, I prefer to indent tasks. So if task A is depending on task B and I intent not to use gulp B command, I’ll indent task B in my gulpfile.js. In our case I’ll indent tasks clean, copy-html, and browserify. Let’s run build command:

Output is interesting and confirms what we were trying to achieve. We can see that clean command was finished first. Then two commands have started: copy-html and browserify at the same time. Then browserify has finished first (because it’s empty now). And copy-html finished right after that. However, these results can be intermittent. If you have your copy-html command finished before browserify - it’s okay, it just means that it can be finished before. Don’t expect specific order for asynchronous commands.

Finally, we can make our server gulp command dependent on build instead of copy-html only:

Full version of gulpfile.js is little bit lengthy now:

Output of gulp server (or npm start) command:

Just great, these two lines are really important here:

We are starting our development server after the build process. We shouldn’t have any issues now. With the server running let’s modify our ./src/index.html to Hello, world! 2. It was not live reloaded in the browser. We still need to watch for changes in ./src folder.

Browse repository at this point of history

Watch for changes

Gulp has plenty of plugins, and here is another one – gulp-watch:

Now we can configure this plugin for our needs. But there is one thing to consider before we can do that. “Watch” means something should be started, and it should watch for changes. So it’s not something you run and forget. You should have process/task/job running. Look at this code from our gulpfile.js:

We pipe everything from ./dist to server, which is gulp-server-livereload module. This module is executed, your console is blocked, because the server is running and serving files, and you need Ctrl+C to stop it. If you add watch task before (like if you add it to dependencies this way: ['build', 'watch']), or after (by piping it next to server) - it won’t work. In the first case development server won’t start, in the second case watch task won’t start.

So we need two tasks to be run at the same time: one will watch for changes, another will start development server. You can use undocumented gulp.start. It’s not a good way to go, so I’m looking for your advice here. In Gulp 4 we should have these things improved and simplified, and we won’t need gulp.start anymore or sequence thing. I would rewrite our server command this way:

Now it should work. I have to admit that I found one interesting (well, expected) gulp-server-livereload behavior here. I had my index.html file with the following content:

I was too lazy to add more tags for the sake of just example, but it was my mistake. gulp-server-livereload will not add magic javascript if you don’t have closing </body> tag. Let’s add it to our index.html:

And run:

Finally, you can change files in your ./src directory and they’ll be live reloaded. gulp-server-livereload adds small javascript magic to every page right before closing </body> tag. Here is how the source of this page looks in my browser:

This JavaScript code loads livereload.js. Code from this file connects to your server app (your gulp task running development server). You’ll see Livereload client connected message every time the client connected.

Try to modify your ./src/index.html to see immediate changes in your browser. So we reached very important point (and have not covered any React stuff yet): now we can change our source, save the file, have it built in a second, and see immediate results in your browser. I think it’s important and it really improves development experience. I wanted to cover this part only because very soon we’ll rely on things like babelify, browserify, bundle and so on. You’ll find yourself very tired of these basic operations without the proper automation. And now we have it. Let’s add our first react component!

Browse repository at this point of history

Adding React component

We want to create very simple one. Something to start from. Greeter component will just greet you on the main page with hello message wrapped in <h1> tag. ./src/greeter.jsx is our first file written with newer version of JavaScript – ES6, and it’s not purely JavaScript, it’s JSX – JavaScript mixed with XML. FYI: you still can use React with JavaScript if you want to. But in this article I won’t cover this topic.

Read this article to learn more about ES6 and ES5 differences. On line 1 we import React namespace from 'react' by using import keyword. On the last line we basically say “export what we have here to default namespace”. There are other ways of exporting your classes, but let’s just leave it this way.

Unfortunately you won’t be able to attach this React file to your page without some magic. We just simply don’t have support for JSX in our browsers. Moreover, it’s not JSX + ES6, and we do not have ES6 support. And even if we’ll have ES6 support, we’ll need React library to be attached to our page, because JavaScript engine needs to know what the hell is React.Component. And even if it know what it is, this React component should be applied somewhere on the page. You may have one thousand of React components in memory, but do you want them to be displayed, and when you want them to be displayed?

I’ll use helper ES6 file app.jsx to load and mount our component to DOM node with id app. Let’s remove Hello, world! and add app node to ./src/index.html:

Our component from ./src/greeter.jsx will be mounted to this node, but we still need ./src/app.jsx helper (and entry point of our application):

Note that this file has no any class definitions and all the statements will be executed right away. We assign greeter variable, and on the last we mount it to our DOM node. If you don’t understand this syntax, you may want to check official Getting Started guide. However, official manual looks little bit outdated (and not sexy to be honest).

My current directory structure:

d336f19a-8251-4b3c-aa70-0e201a0e41e9

As I mentioned before, you can’t just attach ./src/*.jsx files with <script> tag to your page. Let’s convert all of them to one file bundle.js. We’ll need browserify.

Browsers don’t have the require method defined, but Node.js does. With Browserify you can write code that uses require in the same way that you would use it in Node.

Basically, it’s very powerful tool that allows you to use any Node.js module in the browser. It’s little bit more than that, and you can transform (from .jsx to .js), add source maps, combine few modules together, etc. Browserify has it’s CLI, and can be executed both from command line and from your Node.js application (our gulpfile.js). Let’s install it for our project:

We also should install Babelify - transform module for Browserify to be executed at the top level. This module is just a wrapper around Babel compiler that converts your ES6 to JavaScript. Just a reminder: we need it until all the browsers can support ES6 natively. So our dependency chain looks like that:

We don’t need to install Babel directly, only Babelify transform for Browserify:

There is one interesting note from babelify GitHub repo:

As of Babel 6.0.0 there are no plugins included by default.

Babel 6.0.0 has been released on Oct 29, 2015. It is the main reason why your examples from the past won’t work. I spent a day struggling with outdated examples, and repos, and so on.

There is also extremely helpful command from babelify GitHub repo:

(There is also Node.js example, I encourage you to go and take a look at these 6 lines). What the command above can tell us? We execute browserify with -t (transform) switch. We specify the name of transform module (babelify) and parameters to this transform module: --presets [ es2015 react ]. As it was mentioned before “no plugins included by default” in version 6.x of Babel (which is good I think), and we need to install and include them.

But before let’s try to execute the command above without any installed plugins:

What do we see here? This part is really easy:

We execute node_modules/.bin/browserify - it’s our Browserify tool installed locally. Then we pass path to our file(s), it’s src/**/*.jsx in our case (all .jsx files in src directory, including subdirectories), and -o switch – it’s the output file (bundle.js).

Next chunk of magic line is -t [ babelify --presets [ es2015 react ] ], just parameters to use babelify, and parameters for babelify to use presets. But we’re getting

Error: Couldn’t find preset “es2015” relative to directory…

And for us it means that… “no plugins included by default”. Yes, we need to install them:

Note that babel-preset-react is not react itself. Babel is about syntax transformations only. We also need to install react and react-dom libraries, because we have few import directives in our jsx files:

To make React and ReactDOM available in our namespace you need to install them the standard way:

Let’s execute magic command again:

I cheated. You’ll get error message:

Just wanted to follow problem-solution approach for better understanding. In app.jsx we use import directive:

And the module './greeter' can’t be imported. To fix this issue we should use --extension switch. Here is explanation from official docs:

Consider files with specified EXTENSION as modules, this option can used multiple times.

And finally, the true version of our magic line should look like this:

Now you should have bundle.js ready. Mine is 654 KB:

You can manually copy this file to your ./dist folder and attach it with <script> tag:

Be careful while copying, we have clean task in our build process. If you copy it first and then execute gulp server, you’ll get your directory cleaned and you’ll need to copy it again from another terminal window. We’ll automate things in the next section.

Our React component works now:

7308902f-97e0-460f-8a02-a268ffdd9657

Here is the link to repository, but keep in mind that if you clone it, it won’t work, because you won’t have bundle.js in your ./dist folder.

Our new dependencies in package.json:

a540e717-b645-412c-b345-6830a24b35b8

Configure Gulp to do Browserify

To fully automate out build process let’s convert this command:

to Gulp task and then compare results. We already have empty browserify task. Browserify and Babelify are already installed for our app in package.json, so we can do:

And in browserify task we return pre-configured browserify object:

However, it’s not enough. You can run this task, but you won’t see result file. It won’t work because we still need code -o parameter from our command line example. Here is browserify api example:

Look at the last line. What is bundle()? Here is another link to docs:

Bundle the files and their dependencies into a single javascript file. Return a readable stream with the javascript file contents…

Okay, now it makes sense. Let’s try to apply this knowledge to our example:

Great! Now we can see output in the console. All we need to do now is to redirect “readable stream with the javascript file contents” to the file instead of process.stdout. After googling gulp stream to file I found two interesting links:

Official recipe is using vinyl-source-stream (the first link), and it seems the right choice. Basically, it this module redirects conventional text stream into gulp pipeline. So we can take our “readable stream with the javascript file contents” from browserify, and use this “conventional text stream at the start of our gulp pipeline” with vinyl-source-stream. Here is the source code based on examples:

And how these changes look in our task:

Execute gulp to test changes:

Congratulations! Our browserify gulp task is already the part of build task. And it means that we can just npm start, modify our greeter.jsx in our favorite code editor and see immediate changes in the browser! Under the hood gulp will clean dist folder, will babelify ES6 to ES5, will browserify our modules, and will copy html files. Nice work!

2d9e6a2a-d93a-4841-a714-844890e2c313

At the screenshot above I showed you the moment when I saved new file. You can see that build task has been started automatically.

Browse repository at this point of history for Part 1.

All parts

  • [Part 1. Introduction to Modern Frontend Workflow, adding our first React Component] (http://ro31337.github.io/Building-TDD-React-boilerplate-with-ES6-series-Part-1/)
  • Part 2. Setting up tests. Using React Test Utilities. (coming soon)
  • Part 3. Adding more functionality to React component and adding tests. (coming soon)
  • Part 4. Switching to Enzyme. (coming soon)
  • Part 5. Bower and integration with Rails. (coming soon)

About the author:

Roman Pushkin is Web Expert from San Francisco. Roman has over 10 years of web development experience with variety of technologies and frameworks.

Written on February 21, 2016