Recently I’ve been toying with React and trying to find the best way to get up and running. After a lot of trial and error I decided to go with webpack as my build tool and share what I’ve learned from my experience with it.
Initializing a project
Since React doesn’t have any tools to generate and manage projects, it’s up to us to set everything up. We need to create the directory, initialize Git, and initialize npm.
mkdir react-project && cd react-project
git init
npm init
After running npm init
, it’s a good idea to add "private": true
to
package.json
so we can’t accidentally publish our project to npm.
Setting up webpack
Webpack is the build tool that takes our application code and generates static assets as well as a development server. Webpack has several advantages over other build tools: for example, it strips out unused code, supports hot module replacement, and is easily configured.
To get started we need to install webpack both globally and in the project. We
install it globally to make the webpack
command available and we install it
locally so we can specify which version of webpack the project should use
instead of relying on the global install.
npm install webpack --global
npm install webpack --save-dev
Basic configuration
Let’s start with a basic configuration in webpack.config.js
and build on it as
we go.
module.exports = {
context: __dirname + "/app",
entry: "./app.js",
output: {
filename: "app.js",
path: __dirname + "/dist",
},
}
At the moment we’re telling webpack that our application lives in the app
directory and the entry point to our application is app.js
. We also tell
webpack to output the resulting JavaScript in dist/app.js
.
We can add a basic app/app.js
to make sure it’s working:
console.log("webpack rocks!");
Now we can run webpack
again and we can see that it creates dist/app.js
and
includes the code that we wrote.
Adding loaders
At the moment our configuration is basically just copying our file to the dist
folder. The real power of webpack is the loaders that it provides. In this case
we’re going to use loaders to send our code through Babel and transform our
JSX into JavaScript.
Fortunately the Babel loader supports transforming both ES2015 and JSX which
means we can get away with using a single loader instead of requiring both the
babel-loader
and the jsx-loader
.
We can install the babel loader with the following command:
npm install babel-loader --save-dev
To use the loader we need to add a loaders
object nested in a module
object
in webpack.config.js
.
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loaders: ["babel-loader"],
}
],
},
Webpack accepts an array of loader objects which specify loaders
to apply to
files that match the test
regex and exclude files that match the exclude
regex. In this case we’re applying the babel-loader
to all files with a .js
extension that aren’t in node_modules
.
If we run webpack
we’ll see that webpack still bundles our JavaScript
successfully.
Writing React components
Now that we have a basic webpack configuration set up we can add React to the mix. Let’s install React and write a simple greeting component.
First we need to install React:
npm install react --save
Now we can write a greeting component in app/greeting.js
:
import React from "react";
export default React.createClass({
render: function() {
return (
<div className="greeting">
Hello, {this.props.name}!
</div>
);
},
});
Inside app/app.js
we need to import our new greeting component and render it
on the page.
import React from "react";
import Greeting from "./greeting";
React.render(
<Greeting name="World"/>,
document.body
);
We’re using ES6 modules to import React and the greeting component we just
wrote. The first argument to React.render
is using JSX to render our
Greeting
component with the name property being passed in as World
.
JSX is just a more friendly syntax for rendering React components. We could
write it in JavaScript too. When compiled, the babel-loader
will turn our JSX
into the following:
React.createElement(Greeting, {name: "World"})
Now when the application is loaded our Greeting
component will be rendered
with “World” passed in as the name
property.
Adding an index page
React is ready to go, but we need a page to render the content. Let’s create a
basic page in app/index.html
.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack + React</title>
</head>
<body></body>
<script src="app.js"></script>
</html>
Webpack needs to copy this file over to our dist
folder for us to use it which
means we’ll need to modify our entry
property in our webpack config and add an
additional loader.
entry: {
javascript: "./app.js",
html: "./index.html",
},
To actually copy the file over we need the file-loader
package, which is a
loader that copies files.
npm install file-loader --save-dev
In our webpack.config.js
file below our first loader object, we can add a
loader object for the index page:
{
test: /\.html$/,
loader: "file?name=[name].[ext]",
},
Run webpack
one more time and open up the dist/index.html
file in a browser
and you can see our greeting component rendered on the page.
Setting up the webpack dev server
It’s pretty tedious to continue having to type webpack
and refresh the page
each time we make a change. Lucky for us webpack has another module that
provides a development server.
We need to install webpack-dev-server both locally and globally for the same reasons we installed webpack that way. We can install it with the following command:
npm install webpack-dev-server --global
npm install webpack-dev-server --save-dev
To run the dev server run webpack-dev-server
and visit
http://localhost:8080
.
Hot module replacement
Using the webpack-dev-server
we can set up hot module replacement with React.
This means whenever we modify a component and save the file webpack will replace
the module on the page without reloading, without losing component state.
To get hot reloading working with React we have to install react-hot-loader
:
npm install react-hot-loader --save-dev
To use the react-hot-loader
loader, we can just prepend it to the loaders
array in our first loader object.
{
test: /\.js$/,
exclude: /node_modules/,
loaders: ["react-hot", "babel-loader"],
},
Whenever matching files are found they’re now passed through react-hot
first
and then through babel-loader
.
Finally we need to run webpack-dev-server
with two new options:
webpack-dev-server --hot --inline
Now when serving our project webpack injects JavaScript code that connects to the server and waits for module updates to be pushed. When an update is received the module is replaced.
It’s worth noting that not all modules can be replaced. The code in
app/app.js
cannot be reloaded and will cause a full page reload but changing
the Greeting
component will trigger a hot module replacement.
To see it in action we can modify our greeting component by wrapping the text
in an h1
tag. Now the render
function in app/greeting.js
will look like
this:
render: function() {
return (
<div className="greeting">
<h1>Hello, {this.props.name}!</h1>
</div>
);
},
When you save the file you’ll see the page update automatically without a page reload.
For convenience we can add an npm script to run the server with the --hot
--inline
flags so we don’t have to type them out each time.
We can add the command under the scripts
object in package.json
:
"scripts": {
"start": "webpack-dev-server --hot --inline"
},
To run the server all we have to do is run npm start
and we have a development
server with hot module replacement up and running.
What’s next?
- Learn more about webpack loaders.
- Dive deeper into React’s JSX templates.
- Learn about building React applications with Flux, a design pattern by Facebook.