Creating Modular View Components with React and Grunt

by Simon Smith on March 17, 2014

The modern web is always changing, and this article is more than two years old.

By Simon Smith

One of the libraries gaining a lot of interest recently is Facebook’s React JS library. It mainly concerns itself with “the V in MVC” and encourages developers to break their app into resuable, modular components. The Virtual DOM is a great feature on its own, and I encourage you to investigate React if you haven’t yet.

In this post I’ll be explaining how your React components can be used as CommonJS modules and then made browser friendly via Grunt. Using React this way (as opposed to Bower, or just downloading the files manually) allows easy re-use of components on the server as well as all the benefits of modularizing your code.

J to the S to the X

At this point it’s worth noting the use of JSX in React. It transforms easy to read XML-like syntax into normal JS. This means your render method is a lot easier to read and maintain, particularly by less JavaScript savvy developers.

Although it is not required to use React, you’d be a braver man than I to write heavily nested HTML structures without it.

// This easy to read syntax gets translated into...
<div className="Module">
    <h2 className="Module-header h3">{result.login}</h2>
    <Foo className="Foo--simple" link={url} imgUrl={result.avatar_url} text="View profile" />
</div>

// ...this normal not-so-easy to read JavaScript
React.DOM.div( {className:"Module"},
    React.DOM.h2( {className:"Module-header h3"}, result.login),
    Foo( {className:"Foo--simple", link:url, imgUrl:result.avatar_url, text:"View profile"} )
)

A JSX transformer ships with React that can be dropped straight into the browser. This makes prototyping simpler but is also an overhead that should not be used in production.

Seeing as a compile step is required to use CommonJS modules in the browser, it also makes sense to drop in an extra step to compile the JSX. Grunt will be handling both of these steps.

But first…

A few things will be needed from npm before any fun can start.

As well as the above Node modules, a place for the React components and compiled JS file will be needed. I tend to favour a react_components directory (similar to Bower) for the former.

initial-dir

Defining components

For the purpose of this post we’ll pretend that a user profile needs to be created. It will contain an avatar, a bio snippet and allow this data to be passed in as props.

bio.jsx

/** @jsx React.DOM */

var React = require('react');

module.exports = React.createClass({
    render: function() {
        return (
            <div className="Bio">
                <p className="Bio-text">{this.props.text}</p>
            </div>
        )
    }
});

avatar.jsx

/** @jsx React.DOM */

var React = require('react');

module.exports = React.createClass({
    render: function() {
        return (
            <div className="Avatar">
                <img className="Avatar-img" src={this.props.imgSrc} alt="" />
            </div>
        )
    }
});

These are React components at their simplest.

The only things to note are the requiring of the React library at the top and the exporting of the constructor returned from React.createClass. This is the pattern typically used to write components in a CommonJS environment.

With the building blocks defined they can now be easily used to construct a profile component.

profile.jsx

/** @jsx React.DOM */

var React  = require('react');
var Avatar = require('./Avatar.jsx');
var Bio    = require('./Bio.jsx');

module.exports = React.createClass({
    render: function() {
        return (
            <div className="Profile">
                <h2 className="Profile-title">{this.props.username}</h2>
                <div className="Profile-body">
                    <Avatar imgSrc={this.props.avatar} />
                    <Bio text={this.props.bio} />
                </div>
            </div>
        )
    }
});

In this component some Profile specific HTML is added and props are passed on to the Avatar and Bio components.

Bringing the pieces together

All that is left is for a top-level component of some kind to make use of the Profile component. For this example I’ll add an App.jsx component that will pass the necessary data into the Profile and render it in the page.

App.jsx

/** @jsx React.DOM */

var React   = require('react');
var Profile = require('./Profile.jsx');

React.renderComponent(
    <Profile
        username="Simon"
        bio="My name is Simon. I make websites"
        avatar="http://simonsmith.io/assets/images/me.jpg"
    />,
    document.body
);

Overkill?

At this point it may seem unnecessary to break things up to such a granular level. Instead, it may be tempting to just have one Profile component that includes an avatar image and bio text. But what if the application also needs an avatar to be rendered in private messages or a status update?

By creating this level of abstraction, it is trivial to require a few components and start piecing them together. Passing in props or making use of transferPropsTo allows re-use in different contexts whilst keeping all the logic in one place.

Making this modular approach so simple is one of my favourite parts of React.

SuitCSS

The HTML class naming conventions I use come from SuitCSS. The way components are handled there seems to complement React perfectly and I’ve found it very useful to keep the modular approach flowing through the CSS architecture.

Build step

With the components ready to go, the final step is to configure Grunt to compile the JSX and then produce a browser friendly version of the CommonJS modules.

Gruntfile.js

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        watch: {
            react: {
                files: 'react_components/*.jsx',
                tasks: ['browserify']
            }
        },

        browserify: {
            options: {
                transform: [ require('grunt-react').browserify ]
            },
            client: {
                src: ['react_components/**/*.jsx'],
                dest: 'scripts/app.built.js'
            }
        }
    });

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

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

The grunt-browserify task can make use of grunt-react to compile the JSX before producing the final, built JavaScript file. Additionally I’ve included a configuration for the grunt-contrib-watch task to recompile the React components when any of the files are changed.

With the config in place all that is left is to run grunt on the CLI and an app.built.js file will be created in the scripts directory. Drop this into an HTML page and you’re done.

What about AMD?

I’m a massive fan of AMD and RequireJS but haven’t tried it with React as of yet. I’m confident that r.js could be used to convert the JSX compiled templates into AMD modules but I’ll leave that to another post. There is already some discussion around that topic in the React repository.

Wrapping up

The example code used in this post can be found in this GitHub repository. Feel free to drop me a tweet with any feedback/corrections.