Taming the React Setup

Some developers appear to think that setting up React for development is difficult. I believe that this is based on misinformation. In reality, setting up React isn’t all that difficult.

Personally, I worry that a generalization about setting up and learning React is being created based upon the specifics of one particular type of setup (Webpack, Redux, ES6, JSX, Babel etc.), instead of a broad understanding of all of the setup options available.

In this article, I’m going to lay out seven React setups. The majority of the setups I’m going to showcase are, by and large, not all that difficult. However, “bringing your own architecture” (aka BYOA) to any situation is difficult. What do I mean by this? Since React aims to be a view only layer/library, it leaves an open architecture for you to fill in the pieces it does not provide. This openness can make setup difficult, but this isn’t really the fault of React.

The short of it is that React itself is simple, but bringing your own architecture is not. Even BYOA can’t fix the fact that browsers are missing a native JavaScript module loader/bundler. Keep this in mind, as the setups in this article move from simplistic to complex, aiming to fill the gap in the browser.

Obviously, each setup will only scratch the surface of possible configurations. Each setup could be, and should be, evolved to fit the unique particulars of the website or application you are building.

Before discussing the setups, if you are new to React, you should review these terminology I have provided at the end of this article.

Setup 1. Just React, no JSX

Getting set up to run React code that will be rendered to the HTML DOM is dead simple if you’ve decided not to use JSX. You simply include the react.js and react-dom.js scripts into an HTML page and you are ready to write React code.

The react.js file is the core React file needed to create React nodes and write React components. When you intend to render your components into an HTML document (i.e. the DOM), you’ll also need the react-dom.js file. The react-dom.js file is dependent on the react.js file and must be included after first including the react.js file. An example of an HTML document properly including React is shown below.

<!DOCTYPE html>
<html>
  <head>
    <script src="https://fb.me/react-0.14.4.js"></script>
    <script src="https://fb.me/react-dom-0.14.4.js"></script>
  </head>
<body>
</body>
</html>

With the react.js file and react-dom.js file loaded into an HTML page, it is then possible to create React nodes/components and then render them to the DOM. In the HTML below, a HelloMessage React component is created containing a React <div> node that is rendered to the DOM inside of the <div id="app"></div> HTML element.

<!DOCTYPE html>
<html>
    <head>
        <script src="https://fb.me/react-15.0.2.js"></script>
        <script src="https://fb.me/react-dom-15.0.2.js"></script>
    </head>
<body>
    <div id="app"></div>
    <script>
        var HelloMessage = React.createClass({
            displayName: 'HelloMessage',
            render: function render() {
                return React.createElement('div',null,'Hello ',this.props.name);
            }
        });
        ReactDOM.render(React.createElement(HelloMessage,{ name: 'John' }), document.getElementById('app'));
    </script>
</body>
</html>

This setup is all you need to execute React code in ES5 browsers. However, this setup does not make use of JSX or ES 2015.

Setup 2. Transform JSX/ES 2015 in the browser via browser.js (Non-production setup)

An additional script can be added to setup #1 that will allow for the usage of JSX/ES 2015. However, transforming JSX in the client using Babel is not a production-worthy solution. It places the burden of processing JSX/ES 2015 on the client at runtime. However, for non-production situations the browser.js script from the Babel project can be added to an HTML document and provides the means to transform JSX in the client at runtime.

In the HTML file below the HelloMessage component shown in setup #1 has been updated to use JSX.

<!DOCTYPE html>
<html>
    <head>
        <script src="https://fb.me/react-15.0.2.js"></script>
        <script src="https://fb.me/react-dom-15.0.2.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
    </head>
<body>
    <div id="app"></div>
    <script type="text/babel">
        const HelloMessage = React.createClass({
            render: function() {
                return <div>Hello {this.props.name}</div>;
            }
        });

        ReactDOM.render(<HelloMessage name="John" />, document.getElementById('app'));
    </script>
</body>
</html>

The transformation of the code is occurring because we have included the browser.js Babel file and given the <script> element containing our React/JSX code a type attribute of type="text/babel". When browser.js is loaded, it will find any type="text/babel" scripts and transform the script from JSX/ES 2015 to ES5 JavaScript. For example, the script contained in the following HTML document will be transformed to:

var HelloMessage = React.createClass({
    displayName: "HelloMessage",

    render: function render() {
        return React.createElement(
            "div",
            null,
            "Hello ",
            this.props.name
        );
    }
});

ReactDOM.render(React.createElement(HelloMessage, { name: "John" }), document.getElementById('app'));

Note that the Babel project, as of Babel 6, no longer provides browser.js to transform JSX code to ES5 code in the browser. Thus, you will have to use an older version of Babel (i.e. 5.8.23) that provides browser.js for transforming JSX/ES* in the browser.

Setup 3. Transform JSX/ES 2015 in the browser via system.js/browser.js (Non-production setup)

This is going to blow your mind – it did mine. SystemJS, with help from the jspm CDN, will just sort out all of the details for React, JSX, and Babel (i.e. dynamic loading) – in the BROWSER AT RUNTIME!

All you have to do is serve the following HTML file:

<!DOCTYPE html>
<script src="https://jspm.io/system@0.19.js"></script>
<script>
System.config({
  transpiler: 'babel',
  babelOptions: {}
});
System.import('./main.js');
</script>

Which imports the following a main.js file:

import React from 'react'
import ReactDOM from 'react-dom'

const Hello = ({name}) => <h1>Hello {name}!</h1>

ReactDOM.render(
  <Hello name={"dude"}/>,
  document.body.appendChild(document.createElement("div"))
)

And all of that just works. You don’t have to install or download anything. You can try it in this plunker. It will even run locally if you use Firefox.

When the page loads it will acquire and install all the necessary dependencies – including Babel! Check out the source panel from Chrome devtools to see everything that is added.

Screenshot 2016-05-16 14.14.59.png

The jspm CDN works similar to npmcdn.com. It sends source minified over HTTP/2 using depCache injnection, which might even make this approach suitable for production.

Now, you might be thinking this will only work with named jspm packages (i.e. pacakges defined in JSPM registry), but you’d be wrong. You can bypass jspm and also install packages directly from npm or GitHub that are not in the jspm registry. Of course, you have to tell jspm that you are doing this and the package will have to have a main file defined in package.json. For example, you can install the following packages using ES 2015 modules format (go ahead and try it in the plunker from above).

import picturefill from 'github:scottjehl/picturefill'
import async from 'npm:async'

This setup is great for some quick development, but the potential for production use is still unknown given the use of futuristic tools like SPDY and HTTP/2.

Setup 4. Use an online editor to create React pseudocode

Setup #1, #2, and #3 will work with online editors (e.g. jsbin or jsfiddle) when one needs to quickly set up a React environment and share React “pseudocode”.

The fastest and easiest React setup with an online editor can be accomplished with JS Bin. Below, I demonstrate how easy it is to configure JS Bin for React coding.

Setup 5. Transform JSX/ES 2015 during development using Babel-cli and npm scripts

This setup involves using the Babel-cli, Babel presets/plugins, and npm scripts to transform JSX/ES 2015 to ES5 code during development.

We’ll create this setup in seven steps. Alternatively, you can follow the four steps below which use a GitHub repo to accelerate the setup.

  1. Clone/download code
  2. Follow step 1 below
  3. Run npm install from the cloned directory
  4. Follow the last step below

Step 1: Verify Node.js and npm then install global packages

In this step, make sure you have installed or have the most recent stable version of Node.js and npm. Then run the following command to install browser-sync.

> npm install browser-sync -g

You may need to use “sudo” to install the package globally.

Step 2: Create project directory and files

On your local file system create a directory with the following sub-directories and files.

├── index.html
├── jsBuild
├── jsSrc
│    └── app.js
└── package.json

Open the package.json file and place the following empty JSON object inside of it:

{}

Step 3: Install npm devdependencies and dependencies

Open a command prompt from the root of the directory you created in step 2. Then run the following npm commands:

> npm install babel-cli babel-preset-es2015 babel-preset-react babel-preset-stage-0 browser-sync --save-dev

and

> npm install react react-dom --save

Running these two commands will install the necessary npm packages for this setup. The project directory node_modules folder should now contain the following npm packages:

├── index.html
├── jsBuild
├── jsSrc
│   └── app.js
├── node_modules
│   ├── babel-cli
│   ├── babel-preset-es2015
│   ├── babel-preset-react
│   ├── babel-preset-stage-0
│   ├── browser-sync
│   ├── react
│   └── react-dom
└── package.json

Step 4: Configure Babel & npm scripts

Open the package.json file which should look something like this:

{
  "devDependencies": {
    "babel-cli": "^6.8.0",
    "babel-preset-es2015": "^6.6.0",
    "babel-preset-react": "^6.5.0",
    "babel-preset-stage-0": "^6.5.0",
    "browser-sync": "^2.12.5"
  },
  "dependencies": {
    "react": "^15.0.2",
    "react-dom": "^15.0.2"
  }
}

Add the following Babel and scripts configurations to the package.json file.

{
  "scripts": {
    "babel": "babel jsSrc --out-dir jsBuild -w",
    "server": "browser-sync --port 4000 start --server --files \"**/*.html\" \"**/*.css\" \"jsBuild/**/*.js\" "
  },
  "babel": {
    "presets": [
      "es2015",
      "react"
    ],
    "sourceMaps": false
  },
  "devDependencies": {
    "babel-cli": "^6.8.0",
    "babel-preset-es2015": "^6.6.0",
    "babel-preset-react": "^6.5.0",
    "babel-preset-stage-0": "^6.5.0",
    "browser-sync": "^2.12.5"
  },
  "dependencies": {
    "react": "^15.0.2",
    "react-dom": "^15.0.2"
  }
}

These updates configure Babel with the presets we installed from npm and provide two "scripts" that we can run using the npm cli.

Step 5: Update index.html

Open the index.html file and copy the following HTML into the file:

<!DOCTYPE html>
<html>
    <head>
        <title>React via Babel</title>
        <script src="node_modules/react/dist/react.js"></script>
        <script src="node_modules/react-dom/dist/react-dom.js"></script>
    </head>
<body>
    <div id="app"></div>
    <script src="jsBuild/app.js"></script>
</body>
</html>

Note that we are pulling react.js and react-dom.js from the node_modules directory.

Step 6: Update app.js

Open the app.js file and copy the following JavaScript into the file:

const HelloMessage = React.createClass({
    render: function() {
        return <div>Hello {this.props.name}</div>;
    }
});

ReactDOM.render(<HelloMessage name="John" />, document.getElementById('app'));

Step 7: Run Babel and server

From the root of the setup directory, open a command prompt and run the following npm command

> npm run babel

Next, open another new command prompt and run the following npm command

> npm run server

Both of these commands will continue to run while developing.

If you followed all the steps correctly, Browsersync should have opened a browser running the index.html file and app.js file at http://localhost:4000. Both Babel and Browsersync have been configured to re-run when changes are made.

This setup does not assume that you want to build a SPA and only assumes you want to create HTML pages that make use of React, JSX, and ES 2015.

Setup 6. Transform JSX/ES 2015 during development using Babel-core via Webpack

This setup involves using Webpack and several loaders to transform JSX/ES 2015 to ES5 code. By using Webpack, JavaScript modules can be loaded using the ES 2015 module format (commonJS behind the scenes), properly transformed, ad then bundled.

We’ll create this setup in seven steps. Alternatively, you can follow the four steps below which use a GitHub repo to accelerate this setup.

  1. Clone/download the code
  2. Follow step 1 below
  3. Run npm install from the cloned directory
  4. Follow the last step below

Step 1: Verify Node.js and npm then install global packages

In this step, make sure you have installed or have the most recent stable version of Node.js and npm. Then run the following command to install Webpack and browser-sync globally.

> npm install webpack browser-sync -g

You may need to use “sudo” to install the packages globally.

Step 2: Create project directory and files

On your local file system, create a directory with the following sub-directories and files.

├── build
├── index.html
├── package.json
├── src
│   ├── app.js
│   ├── app.css
│   └── math.js
└── webpack.config.js

Open the package.json file and place the following empty JSON object inside of it:

{}

Step 3: Install npm devdependencies and dependencies

Open a command prompt from the root of the directory you created in step 2. Then run the following npm commands:

> npm install babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-0 browser-sync css-loader extract-text-webpack-plugin style-loader webpack --save-dev

Next run:

> npm install react react-dom @telerik/kendo-react-buttons --save

Running these two commands will install the necessary npm packages for this setup. The project directory node_modules folder should now contain the following npm packages:

├── build
├── index.html
├── node_modules
│   ├── @telerik
│   ├── babel-core
│   ├── babel-loader
│   ├── babel-preset-es2015
│   ├── babel-preset-react
│   ├── babel-preset-stage-0
│   ├── browser-sync
│   ├── css-loader
│   ├── extract-text-webpack-plugin
│   ├── react
│   ├── react-dom
│   ├── style-loader
│   └── webpack
├── package.json
├── src
│   ├── app.js
│   ├── app.css
│   └── math.js
└── webpack.config.js

Step 4: Update app.js, app.css, math.js, and index.html

Open app.js and add the following to the file:

import React from 'react';
import ReactDOM from 'react-dom';
import * as KendoReactButtons from '@telerik/kendo-react-buttons';
import '@telerik/kendo-react-buttons/dist/npm/css/main.css';
import { square, diag } from './math.js';
import './app.css';

console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

class ButtonContainer extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            disabled: false
        };
    }
    onClick = () => {
        this.setState({ disabled: !this.state.disabled });
    }
    render() {
        return (
            <div>
                <KendoReactButtons.Button onClick={this.onClick}>Button 1</KendoReactButtons.Button>
                <KendoReactButtons.Button disabled={this.state.disabled}>Button 2</KendoReactButtons.Button>
            </div>
        );
    }
}

ReactDOM.render(
    <div>
        <p>Button</p>
        <KendoReactButtons.Button>Button test</KendoReactButtons.Button>
        <p>Disabled Button</p>
        <KendoReactButtons.Button disabled>Button</KendoReactButtons.Button>
        <p>Primary Button</p>
        <KendoReactButtons.Button primary>Primary Button</KendoReactButtons.Button>
        <p>Button with icon</p>
        <KendoReactButtons.Button icon="refresh">Refresh</KendoReactButtons.Button>
        <p>Button with icon (imageUrl)</p>
        <KendoReactButtons.Button imageUrl="http://demos.telerik.com/kendo-ui/content/shared/icons/sports/snowboarding.png">Snowboarding</KendoReactButtons.Button>
        <p>Button with a custom icon (iconClass) [FontAwesome icon]</p>
        <KendoReactButtons.Button iconClass="fa fa-key fa-fw">FontAwesome icon</KendoReactButtons.Button>
        <p>Toggleable Button</p>
        <KendoReactButtons.Button togglable>Togglable button</KendoReactButtons.Button>
        <p>onClick event handler</p>
        <ButtonContainer />
    </div>,
    document.getElementById('app')
);

Open app.css and add the following to the file:

body{
    margin:50px;
}

Open math.js and add the following to the file:

export const sqrt = Math.sqrt;

export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

Open index.html and add the following to the file:

<!DOCTYPE html>
<html>
    <head>
        <title>webpack</title>
         <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
        <link rel="stylesheet" type="text/css" href="./build/style.css">
    </head>
<body>
    <div id="app"></div>
    <script src="./build/appBundle.js"></script>
</body>
</html>

Step 5: Update webpack.config.js

Open webpack.config.js and add the following to the file:

var path = require('path');
var ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    entry: ['./src/app.js'],
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'appBundle.js'
    },
    module: {
        loaders: [{
            test: /\.css$/,
            loader: ExtractTextPlugin.extract("style-loader", "css-loader")
        }, {
            loader: 'babel-loader',
            exclude: /node_modules/,
            test: /\.js$/,
            query: {
                presets: ['es2015', 'react', 'stage-0'],
            },
        }]
    },
    plugins: [
        new ExtractTextPlugin("style.css", {
            allChunks: true
        })
    ]
};

Step 6: Update package.json

Open the package.json file which should look something like this:

{
  "devDependencies": {
    "babel-core": "^6.8.0",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.6.0",
    "babel-preset-react": "^6.5.0",
    "babel-preset-stage-0": "^6.5.0",
    "browser-sync": "^2.12.7",
    "css-loader": "^0.23.1",
    "extract-text-webpack-plugin": "^1.0.1",
    "style-loader": "^0.13.1",
    "webpack": "^1.13.0"
  },
  "dependencies": {
    "@telerik/kendo-react-buttons": "^0.1.0",
    "react": "^15.0.2",
    "react-dom": "^15.0.2"
  }
}

Add the following scripts configurations to the package.json file.

{
  "scripts": {
    "webpack": "webpack --watch",
    "server": "browser-sync --port 4000 start --server --files \"**/*.html\" \"build/**/*.css\" \"build/**/*.js\" "
  },
  "devDependencies": {
    "babel-core": "^6.8.0",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.6.0",
    "babel-preset-react": "^6.5.0",
    "babel-preset-stage-0": "^6.5.0",
    "browser-sync": "^2.12.7",
    "css-loader": "^0.23.1",
    "extract-text-webpack-plugin": "^1.0.1",
    "style-loader": "^0.13.1",
    "webpack": "^1.13.0"
  },
  "dependencies": {
    "@telerik/kendo-react-buttons": "^0.1.0",
    "react": "^15.0.2",
    "react-dom": "^15.0.2"
  }
}

This update provides two "scripts" we can run using the npm cli.

Step 7: Run Webpack and server

From the root of the setup directory, open a command prompt and run the following npm command:

> npm run webpack

Next, open another new command prompt and run the following npm command:

> npm run server

Both of these commands will continue to run while developing.

If you followed all the steps correctly, Browsersync should have opened a browser running the index.html file and app.js file at http://localhost:4000. Both Webpack and Browsersync have been configured to re-run when changes are made.

This setup is just the tip of the iceberg. Depending upon the scope and scale of the application you are building, this basic Webpack setup could be configured and reconfigured in many ways. Start with this basic setup, study Webpack in-depth, and slowly scale it from here. Be very careful about the complexity you add to this setup, as you risk unintentionally creating a house of cards.

Setup 7. Transform JSX/ES 2015 during development using Babel-core via SystemJS/jspm.io

This React setup involves using systemJS/jspm-cli to tranform (JSX/ES 2015), load, and bundle JavaScript modules (and CSS) using the ES 2015 module format.

I think we have saved the best for last. Mostly because systemJS/jspm handles the configuration file with a cli tool and the solution would appear to be the most future proof offering available today.

We’ll create this setup in nine steps. Alternatively, you can follow the four steps below which use a GitHub repo to speed up this setup.

  1. Clone/download the code
  2. Follow step 1 below
  3. Run npm install && jspm install from the cloned directory
  4. Follow step 8 below.

Step 1: Verify Node.js and npm then install global packages

In this step, make sure you have installed or have the most recent stable version of Node.js and npm. Then run the following command to install jspm and browser-sync globally.

> npm install jspm browser-sync -g

Step 2: Create project directory and files

On your local file system create a directory with the following sub-directories and files.

├── app.js
├── index.html
├── js
│   └── math.js
├── package.json
└── style
    └── app.css

Open the package.json file and place the following empty JSON object inside of it:

{}

Step 3: Install npm devdependencies

Open a command prompt from the root of the directory that you created in step 2. Run the following npm commands:

> npm install jspm browser-sync --save-dev

Running this command will install the necessary npm packages for this setup. The project directory node_modules folder should now contain the following npm packages:

├── app.js
├── index.html
├── js
│   └── math.js
├── node_modules
│   ├── browser-sync
│   └── jspm
├── package.json
└── style
    └── app.css

Step 4: Initiate a SystemJS/jspm setup

Open a command prompt from the root of the directory you created in step 2. Then run the following jspm-cli commands:

> jspm init

This will ask you 9 questions, just hit enter for each question.

Package.json file does not exist, create it? [yes]:
Would you like jspm to prefix the jspm package.json properties under jspm? [yes]:
Enter server baseURL (public folder path) [./]:
Enter jspm packages folder [./jspm_packages]:
Enter config file path [./config.js]:
Configuration file config.js doesn't exist, create it? [yes]:
Enter client baseURL (public folder URL) [/]:
Do you wish to use a transpiler? [yes]:
Which ES6 transpiler would you like to use, Babel, TypeScript or Traceur? [babel]:

This will create a config.js and jspm_packagees directory (with default packages) for you. The setup directory should look like this:

├── app.js
├── config.js
├── index.html
├── js
│   └── math.js
├── jspm_packages
│   ├── github
│   ├── npm
│   ├── system-csp-production.js
│   ├── system-csp-production.js.map
│   ├── system-csp-production.src.js
│   ├── system-polyfills.js
│   ├── system-polyfills.js.map
│   ├── system-polyfills.src.js
│   ├── system.js
│   ├── system.js.map
│   └── system.src.js
├── node_modules
│   ├── browser-sync
│   └── jspm
├── package.json
└── style
    └── app.css

Open config.js and change the babelOptions object from:

babelOptions: {
    "optional": [
        "runtime",
        "optimisation.modules.system"
    ]
},

to:

babelOptions: {
    "optional": [
      "runtime",
      "optimisation.modules.system"
    ],
    "stage": 0
},

Step 5: Update app.js, app.css, math.js, and index.html

Open app.js and add the following to the file:

import './style/app.css!'; //note, had to add the !
import React from 'react';
import ReactDOM from 'react-dom';
import * as KendoReactButtons from '@telerik/kendo-react-buttons';
import '@telerik/kendo-react-buttons/dist/npm/css/main.css!'; //note, had to add the !
import { square, diag } from './js/math.js';

console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

class ButtonContainer extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            disabled: false
        };
    }
    onClick = () => {
        this.setState({ disabled: !this.state.disabled });
    }
    render() {
        return (
            <div>
                <KendoReactButtons.Button onClick={this.onClick}>Button 1</KendoReactButtons.Button>
                <KendoReactButtons.Button disabled={this.state.disabled}>Button 2</KendoReactButtons.Button>
            </div>
        );
    }
}

ReactDOM.render(
    <div>
        <p>Button</p>
        <KendoReactButtons.Button>Button test</KendoReactButtons.Button>
        <p>Disabled Button</p>
        <KendoReactButtons.Button disabled>Button</KendoReactButtons.Button>
        <p>Primary Button</p>
        <KendoReactButtons.Button primary>Primary Button</KendoReactButtons.Button>
        <p>Button with icon</p>
        <KendoReactButtons.Button icon="refresh">Refresh</KendoReactButtons.Button>
        <p>Button with icon (imageUrl)</p>
        <KendoReactButtons.Button imageUrl="http://demos.telerik.com/kendo-ui/content/shared/icons/sports/snowboarding.png">Snowboarding</KendoReactButtons.Button>
        <p>Button with a custom icon (iconClass) [FontAwesome icon]</p>
        <KendoReactButtons.Button iconClass="fa fa-key fa-fw">FontAwesome icon</KendoReactButtons.Button>
        <p>Toggleable Button</p>
        <KendoReactButtons.Button togglable>Togglable button</KendoReactButtons.Button>
        <p>onClick event handler</p>
        <ButtonContainer />
    </div>,
    document.getElementById('app')
);

Open app.css and add the following to the file:

body{
    margin:50px;
}

Open math.js and add the following to the file:

export const sqrt = Math.sqrt;

export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

Open index.html and add the following to the file:

<!DOCTYPE html>
<html>
    <head>
        <title>systemJS/jspm</title>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
    </head>
<body>
    <div id="app"></div>
    <script src="jspm_packages/system.js"></script>
    <script src="config.js"></script>
    <script>
            System.import('app.js');
    </script>
</body>
</html>

Step 6: Install development packages using jspm-cli

Open a command prompt from the root of the directory you created in step 2. Then run the following jspm-cli command:

> jspm install react react-dom css npm:@telerik/kendo-react-buttons

This might confuse some people, so let me clarify that by using jspm you are now installing jspm, npm, and GitHub packages using the jspm-cli and not the npm command line tool.

The above command will install React, react-dom, a jspm css plugin, and the Kendo UI React buttons in the jspm_packages folder. These dependencies are documented automatically in the package.json file. Additionally, the jspm configuration file is updated so that the installed packages can be used without having to manually update the config.js file.

The updated jspm_packages folder will now look like this:

├── jspm_packages
│   ├── github
│   │   ├── jspm
│   │   └── systemjs
│   ├── npm
│   │   ├── @telerik
│   │   ├── Base64@0.2.1
│   │   ├── Base64@0.2.1.js
│   │   ├── asap@2.0.3
│   │   ├── asap@2.0.3.js
│   │   ├── assert@1.3.0
│   │   ├── assert@1.3.0.js
│   │   ├── babel-core@5.8.38
│   │   ├── babel-core@5.8.38.js
│   │   ├── babel-runtime@5.8.38
│   │   ├── base64-js@0.0.8
│   │   ├── base64-js@0.0.8.js
│   │   ├── browserify-zlib@0.1.4
│   │   ├── browserify-zlib@0.1.4.js
│   │   ├── buffer@3.6.0
│   │   ├── buffer@3.6.0.js
│   │   ├── classnames@2.2.5
│   │   ├── classnames@2.2.5.js
│   │   ├── core-js@1.2.6
│   │   ├── core-js@1.2.6.js
│   │   ├── core-util-is@1.0.2
│   │   ├── core-util-is@1.0.2.js
│   │   ├── domain-browser@1.1.7
│   │   ├── domain-browser@1.1.7.js
│   │   ├── encoding@0.1.12
│   │   ├── encoding@0.1.12.js
│   │   ├── events@1.0.2
│   │   ├── events@1.0.2.js
│   │   ├── fbjs@0.6.1
│   │   ├── fbjs@0.6.1.js
│   │   ├── fbjs@0.8.2
│   │   ├── fbjs@0.8.2.js
│   │   ├── https-browserify@0.0.0
│   │   ├── https-browserify@0.0.0.js
│   │   ├── iconv-lite@0.4.13
│   │   ├── iconv-lite@0.4.13.js
│   │   ├── ieee754@1.1.6
│   │   ├── ieee754@1.1.6.js
│   │   ├── inherits@2.0.1
│   │   ├── inherits@2.0.1.js
│   │   ├── is-stream@1.1.0
│   │   ├── is-stream@1.1.0.js
│   │   ├── isarray@0.0.1
│   │   ├── isarray@0.0.1.js
│   │   ├── isarray@1.0.0
│   │   ├── isarray@1.0.0.js
│   │   ├── isomorphic-fetch@2.2.1
│   │   ├── isomorphic-fetch@2.2.1.js
│   │   ├── js-tokens@1.0.3
│   │   ├── js-tokens@1.0.3.js
│   │   ├── loose-envify@1.2.0
│   │   ├── loose-envify@1.2.0.js
│   │   ├── node-fetch@1.5.2
│   │   ├── node-fetch@1.5.2.js
│   │   ├── object-assign@4.1.0
│   │   ├── object-assign@4.1.0.js
│   │   ├── pako@0.2.8
│   │   ├── pako@0.2.8.js
│   │   ├── path-browserify@0.0.0
│   │   ├── path-browserify@0.0.0.js
│   │   ├── process-nextick-args@1.0.7
│   │   ├── process-nextick-args@1.0.7.js
│   │   ├── process@0.11.3
│   │   ├── process@0.11.3.js
│   │   ├── promise@7.1.1
│   │   ├── promise@7.1.1.js
│   │   ├── punycode@1.3.2
│   │   ├── punycode@1.3.2.js
│   │   ├── querystring@0.2.0
│   │   ├── querystring@0.2.0.js
│   │   ├── react-dom@0.14.8
│   │   ├── react-dom@0.14.8.js
│   │   ├── react-dom@15.0.2
│   │   ├── react-dom@15.0.2.js
│   │   ├── react@0.14.8
│   │   ├── react@0.14.8.js
│   │   ├── react@15.0.2
│   │   ├── react@15.0.2.js
│   │   ├── readable-stream@1.1.14
│   │   ├── readable-stream@1.1.14.js
│   │   ├── readable-stream@2.1.2
│   │   ├── readable-stream@2.1.2.js
│   │   ├── stream-browserify@1.0.0
│   │   ├── stream-browserify@1.0.0.js
│   │   ├── string_decoder@0.10.31
│   │   ├── string_decoder@0.10.31.js
│   │   ├── ua-parser-js@0.7.10
│   │   ├── ua-parser-js@0.7.10.js
│   │   ├── url@0.10.3
│   │   ├── url@0.10.3.js
│   │   ├── util-deprecate@1.0.2
│   │   ├── util-deprecate@1.0.2.js
│   │   ├── util@0.10.3
│   │   ├── util@0.10.3.js
│   │   ├── whatwg-fetch@1.0.0
│   │   └── whatwg-fetch@1.0.0.js
│   ├── system-csp-production.js
│   ├── system-csp-production.js.map
│   ├── system-csp-production.src.js
│   ├── system-polyfills.js
│   ├── system-polyfills.js.map
│   ├── system-polyfills.src.js
│   ├── system.js
│   ├── system.js.map
│   └── system.src.js

Step 7: Update package.json

Open the package.json file which should look something like this:

{
  "devDependencies": {
    "browser-sync": "^2.12.8",
    "jspm": "^0.16.34"
  },
  "jspm": {
    "dependencies": {
      "@telerik/kendo-react-buttons": "npm:@telerik/kendo-react-buttons@^0.1.0",
      "css": "github:systemjs/plugin-css@^0.1.21",
      "react": "npm:react@^15.0.2",
      "react-dom": "npm:react-dom@^15.0.2"
    },
    "devDependencies": {
      "babel": "npm:babel-core@^5.8.24",
      "babel-runtime": "npm:babel-runtime@^5.8.24",
      "core-js": "npm:core-js@^1.1.4"
    }
  }
}

Add the following scripts configurations to the package.json file.

{
  "scripts": {
    "bundle": "jspm bundle app.js --inject",
    "unBundle": "jspm unbundle",
    "server": "browser-sync --port 4000 --no-inject-changes start --server --files \"**/*.html\" \"style/**/*.css\" \"js/**/*.js\" "
  },
  "devDependencies": {
    "browser-sync": "^2.12.8",
    "jspm": "^0.16.34"
  },
  "jspm": {
    "dependencies": {
      "@telerik/kendo-react-buttons": "npm:@telerik/kendo-react-buttons@^0.1.0",
      "css": "github:systemjs/plugin-css@^0.1.21",
      "react": "npm:react@^15.0.2",
      "react-dom": "npm:react-dom@^15.0.2"
    },
    "devDependencies": {
      "babel": "npm:babel-core@^5.8.24",
      "babel-runtime": "npm:babel-runtime@^5.8.24",
      "core-js": "npm:core-js@^1.1.4"
    }
  }
}

This update provides two "scripts" we can run using the npm cli.

Step 8: Run server

From the root of the setup directory open a command prompt and run the following npm command:

> npm run server

If you followed all the steps correctly Browsersync should have open a browser running the index.html file and app.js file at http://localhost:4000. Browsersync has been configured to re-run when changes are made.

Step 9: Bundle mode

SystemJS/jspm offers a bundled mode. From the root of the setup directory open a command prompt and run the following npm command:

> npm run bundle

By running this command, the browser should reload and be running from a build.js file that has been created for you in the root of the setup directory. Additionally, the bundling process will combine and in-line into the HTML document any CSS that was imported in a module (e.g. app.css)

To unbundle simply run:

> npm run unBundle

Now Go Use React

Hopefully, one of these seven setups will get you going with React without issues. I’d resist the urge to go and grab a large React boilerplate or starter kit unless the setup is as simple as the ones found here. When dealing with a BYOA situation, always start small and question every layer of complexity.

Now go use React, the setup should not be blocking you.

Related Resources

Addendum – Terminology

Babel

Babel transforms JavaScript ES* (i.e. JS 2016, 2016, 2017) to ES5. Babel is the tool of choice from the React team for writing future ES* and transforming JSX to ES5 code.

Babel CLI

Babel comes with a CLI tool, called Babel CLI, that can be used to compile files from the command line.

Document Object Model (aka DOM)

“The Document Object Model (DOM) is a programming interface for HTML, XML and SVG documents. It provides a structured representation of the document as a tree. The DOM defines methods that allow access to the tree, so that they can change the document structure, style and content. The DOM provides a representation of the document as a structured group of nodes and objects, possessing various properties and methods. Nodes can also have event handlers attached to them, and once an event is triggered, the event handlers get executed. Essentially, it connects web pages to scripts or programming languages.” – MSD

ES5

The 5th edition of the ECMAScript standard. The ECMAScript 5.1 edition was finalized on June 2011.

ES6/ES 2015

The 6th edition of the ECMAScript standard. AKA JavaScript 2015. The ECMAScript 6th edition was finalized on June 2015.

ECMAScript 2016 (aka ES7)

Name of the specification that will provide updates to the JavaScript language in 2016.

ES*

Used to represent the current version of JavaScript, as well as potential future versions that can be written today using tools like Babel. When you see “ES*” it more than likely means you’ll find uses of ES5, ES6, and ES7 together.

jspm.io

jspm is a package manager for the SystemJS universal module loader. It can Load any module format (ES6, AMD, CommonJS and globals) directly from any registry such as npm and GitHub with flat version-ed dependency management.

JSX

JSX is an optional XML-like syntax extension to ECMAScript that can be used to define an HTML-like tree structure in a JavaScript file. The JSX expressions in a JavaScript file must be transformed to JavaScript syntax before a JavaScript engine can parse the file. Babel is typically used, and recommended for transforming JSX expressions.

Node.js

An open-source, cross-platform runtime environment for writing JavaScript. The runtime environment interprets JavaScript using Google’s V8 JavaScript engine.

npm

The package manager for JavaScript born from the Node.js community.

systemJS

Module loader that loads, ES6 modules, AMD, CommonJS and global scripts in the browser and NodeJS. Works with both Traceur, Babel, and Typescript.

Webpack

A module loader and bundler that takes modules (.js, .css, .txt, etc…) with dependencies and generates static assets representing those modules.

Header image courtesy of www.david baxendale.com

Comments

  • Pingback: Dew Drop – May 25, 2016 (#2259) | Morning Dew()

  • Victor Longon

    i have a similar setup as number 6. But I use browsersync for proxying plus hot loading and sass compiling for sass (so I can include sass partial files directly on my components).
    Thanks fot sharing!

  • A follow up to this article showing how to use nwb (React cli scaffolding app tool) to setup up a React app that uses new Kendo UI React components, “Easily Set Up Kendo UI for React Development with nwb” http://www.telerik.com/blogs/easily-set-up-kendo-ui-for-react-development-with-nwb

  • brownieboy

    Re: Setup 3. My mind definitely blown! With the demise of JSX Tools and then Babel’s browser.js inline transpilers, I guess this is the best we have that doesn’t involve a full build setup. Although its achilles heel is that it won’t work if you’re offline.

    Re: Setup 7. I clone the git repo then followed your (shortened) steps from there. It didn’t work for me. The index.html loads up but it can’t find build.js thereafter.

    Also, I’m not sure what advantages Setup 7 gives you over a Webpack-based approach (your Setup 6). Involving JSPM as an extra set of configs on top of NPM is too much extra work for not much gain, IMHO.

    • Point 1. Yup, you’d have to have a backup plan.

      Point 2. Anything more you can tell me?

      Point 3. The advantage is jspm manages the config file for you while webpack does not. That is what the jspm cli does. Make sense?

  • Joachim

    a question: how can one figure out which version to use for the various dependencies in packages.json, to avoid API version conflicts..?