Setting up application with Webpack 3.0 + ES6 + React

Writing this blog to give clear understanding what all you need to get started with this application being written using webpack with React JS.

Lets get started(i hope you already have node JS installed and used to with NPM )

Step : 1 create package.json using npm init

Init

Create a new folder, and package.json file in it, with the following content:

{
"name": "webpack-react"
}

Of course you can replace “webpack-react” with your project name.

Show file structure after this step

Webpack

webpack is a module bundler for modern JavaScript applications. When webpack processes your application, it recursively builds a dependency graph that includes every module your application needs, then packages all of those modules into a small number of bundles — often only one — to be loaded by the browser.

We’ll start with installing webpack for module bundling. It will transpile and bundle our JavaScript files, compile SASS or PostCSS, optimize images… and a bunch of other things like different type of files font files/text files/html & json.

npm install --save-dev webpack
npm install -g webpack // install it globally also

Then we need some modules. We’ll keep our source files in src folder, so we need to create it. Then create js folder in the src folder you just created, and app.js file in it.

Webpack is command line tool once you install it globally using npm install -g webpack you can access it from command line.

webpack  ./src/js/app.js --output-filename ./dist/app.bundle.js

If you open generated app.bundle.js you’ll see webpack’s module handling code at the top, and at the end you’ll find our modest console.log. Webpack’s code does all the work with modules

lets write webpck.config.js where we can sum up all things together required for bundling.

const path = require('path');
const paths = {
DIST: path.resolve(__dirname, 'dist'),
JS: path.resolve(__dirname, 'src/js'),
};
// Webpack configuration
module.exports = {
entry: path.join(paths.JS, 'app.js'),
output: {
path: paths.DIST,
filename: 'app.bundle.js'
},
};

You’ll see that we added our app.js as entry and for the output we selected dist folder and app.bundle.js as the filename.

Now we can run webpack without inline configuration. By default webpack looks for webpack.config.js and reads config from it.

webpack 

This command has the exact same result as the first one. Now let’s try to make it even nicer with adding npm scripts

Open package.json which at this point should look like this:

{
"name": "webpack-react",
"devDependencies": {
"webpack": "^3.6.0"
}
}

We’ll add script section. In this sections, let’s add build task with only one command - webpack.

{
"name": "webpack-react",
"devDependencies": {
"webpack": "^3.6.0"
},
"scripts": {
"build": "webpack"
}
}
// just execute npm run build 

Now we can run it using:

npm run build

Npm tasks allow us not to type full path to the package binary every time. It searches for locally installed packages in the project’s node_modulesfolder.

Again, it has the same result like the previous command, but it is cleaner as it uses npm tasks instead of bare terminal commands.

Now we have our simple build process same we can add other tasks in out scripts section of package json.

Show file structure after this step

Webpack dev server

To be able to open our application in a browser, we’ll need a server. Webpack already provides us with a dev server.Let’s install it:

npm install --save-dev webpack-dev-server

Let’s update package.json right away to make it easier to run dev server.

{
"name": "webpack-react",
"devDependencies": {
"webpack": "^3.6.0",
"webpack-dev-server": "^2.9.1"
},
"scripts": {
"start": "webpack-dev-server",
"build": "webpack"
}
}

now start webpack-dev-server which will run on some default port and will work like local server & will deploy our application http://localhost:8080.

npm run start

let’s create a simple index.html in the src folder

<!doctype html>
<html>
<head>
<title>Webpack React</title>
</head>
<body>
</body>
</html>

and update webpack.config.js to use src as a content base.

const path = require('path');
// Constant with our paths
const paths = {
DIST: path.resolve(__dirname, 'dist'),
SRC: path.resolve(__dirname, 'src'), // source folder path ->
JS: path.resolve(__dirname, 'src/js'),
};
// Webpack configuration
module.exports = {
entry: path.join(paths.JS, 'app.js'),
output: {
path: paths.DIST,
filename: 'app.bundle.js',
},
// Dev server configuration -> ADDED IN THIS STEP
// Now it uses our "src" folder as a starting point
devServer: {
contentBase: paths.SRC,
},

};

Restart npm run start and visit http://localhost:8080, it will just show a blank page. No signs of our JavaScript. To automatically inject <script>tags with our bundled application we’ll use html-webpack-plugin.

This is a webpack plugin that simplifies creation of HTML files to serve your webpack bundles. This is especially useful for webpack bundles that include a hash in the filename which changes every compilation. You can either let the plugin generate an HTML file for you,

HTML Webpack Plugin

This plugin simplifies creation of HTML files to serve your webpack bundles. Let’s install it:

npm install --save-dev html-webpack-plugin

Once installed we need to activate it in webpack.config.js. Require it and add it to the plugins section of the config:

We don’t need contentBase: paths.SRC anymore as it will be handled by html plugin. So we’ll remove whole devServer configuration object for now.

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // Import our plugin -> ADDED IN THIS STEP
// Constant with our paths
const paths = {
DIST: path.resolve(__dirname, 'dist'),
SRC: path.resolve(__dirname, 'src'),
JS: path.resolve(__dirname, 'src/js'),
};
// Webpack configuration
module.exports = {
entry: path.join(paths.JS, 'app.js'),
output: {
path: paths.DIST,
filename: 'app.bundle.js',
},
// Tell webpack to use html plugin -> ADDED IN THIS STEP
// index.html is used as a template in which it'll inject bundled app.
plugins: [
new HtmlWebpackPlugin({
template: path.join(paths.SRC, 'index.html'),
}),
],

};

When we restart dev task, we’ll be able to see Hello world! in the console. Now we are talking! We can start adding some modern JavaScript.

Show file structure after this step

Babel

To be able to use ES2015 and beyond we’ll need to provide a transpiler. Our choice is Babel. Babel takes modern JavaScript and transpiles it — converts it to the old version of JavaScript that can be executed in the browsers that don’t support modern JavaScript standards.

We need this for two reasons (and you probably know both already):

  • React and JSX heavily rely on modern JavaScript features
  • You should use modern JavaScript — it makes things easier and helps you write better code

Let’s continue by installing four packages:

  • Babel core package
  • Babel webpack loader
  • Babel env preset 3
  • Babel React preset
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react

Babel also has default config file which is .babelrc, so let’s create it in our project’s root:

{
"presets": ["es2015", "react"]
}

This will tell Babel to use two presets we just installed.

Now we need to update webpack.config.js to use Babel loader for .jsand .jsx files. We also added some sugar, so you can import those files without specifying file extension.

// We are using node's native package 'path'
// https://nodejs.org/api/path.html
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// Constant with our paths
const paths = {
DIST: path.resolve(__dirname, 'dist'),
SRC: path.resolve(__dirname, 'src'),
JS: path.resolve(__dirname, 'src/js'),
};
// Webpack configuration
module.exports = {
entry: path.join(paths.JS, 'app.js'),
output: {
path: paths.DIST,
filename: 'app.bundle.js',
},
// Tell webpack to use html plugin
plugins: [
new HtmlWebpackPlugin({
template: path.join(paths.SRC, 'index.html'),
}),
],
// Loaders configuration -> ADDED IN THIS STEP
// We are telling webpack to use "babel-loader" for .js and .jsx files
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
'babel-loader',
],
},

],
},
resolve: {
extensions: ['.js', '.jsx'],
},
};

Restart npm run start once again. Nothing really changed, but our JavaScript is now transpiled, and if you used any of the modern JavaScript features, those would be transpiled to ES5 synthax.

Finally let’s add React.

Show file structure after this step

React

This is probably the main reason you are reading this, so I’ll assume you are already familiar with React.

Install it (this time as a regular dependency):

npm install --save react react-dom

Let’s add div with id app to index.html, in which we’ll render our React app.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
    <title>Webpack Babel React revisited</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

Finally replace console.log in our app.js with a real React component.

import React, { Component } from 'react';
import { render } from 'react-dom';
export default class Hello extends Component {
render() {
return (
<div>
Hello from react es6
</div>
);
}
}
render(<Hello />, document.getElementById('root'));

Restart dev server one more time, and voala, we have our React app running!

This is minimal working setup

At this point, you have bare bones setup for making React apps using Webpack and Babel. You can start exploring on your own, add more stuff and modify it to fit your needs. However in this post I’ll cover two more things — CSS and assets loaders.

CSS

Every web application needs CSS. So let’s add a way of getting CSS into ours. Create src/css folder and a simple style.css in it.

body {
background: #f9fafb;
font-family: Helvetica, Arial, sans-serif;
font-size: 16px;
margin: 0;
padding: 30px;
}

To add this CSS file to the app, we’ll use css-loader.

CSS loader needs to write loaded CSS code to either style tag in the head or external stylesheet file. If you want to write it to the style tag you should use style-loader.

But for now, we’ll extract it to the external file by using extract-text-webpack-plugin. HTML webpack plugin, that we already set, will add css file to index.html for us.

Again, start by installing packages:

npm install --save-dev css-loader extract-text-webpack-plugin

We’ll need to do two more things:

  • import our CSS in app.js:
import React, { Component } from 'react';
import { render } from 'react-dom';
import '../css/style.css'; // Import CSS -> ADDED IN THIS STEP
export default class Hello extends Component {
render() {
return (
<div>
Hello from react
</div>
);
}
}
render(<Hello />, document.getElementById('app'));
  • and update webpack config to use css-loade/style-loader for CSS files:
// We are using node's native package 'path'
// https://nodejs.org/api/path.html
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); // -> ADDED IN THIS STEP
// Constant with our paths
const paths = {
DIST: path.resolve(__dirname, 'dist'),
SRC: path.resolve(__dirname, 'src'),
JS: path.resolve(__dirname, 'src/js'),
};
// Webpack configuration
module.exports = {
entry: path.join(paths.JS, 'app.js'),
output: {
path: paths.DIST,
filename: 'app.bundle.js',
},
// Tell webpack to use html plugin
plugins: [
new HtmlWebpackPlugin({
template: path.join(paths.SRC, 'index.html'),
}),
new ExtractTextPlugin('style.bundle.css'), // CSS will be extracted to this bundle file -> ADDED IN THIS STEP
],
// Loaders configuration
// We are telling webpack to use "babel-loader" for .js and .jsx files
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
'babel-loader',
],
},
// CSS loader to CSS files -> ADDED IN THIS STEP
// Files will get handled by css loader and then passed to the extract text plugin
// which will write it to the file we defined above
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
use: 'css-loader',
}),
}
],
},
resolve: {
extensions: ['.js', '.jsx'],
},
};

This might seem very complicated to get one CSS file to the page, but it is very useful when you have multiple stylesheet files and pre-processing (or post-processing).

Restart npm run start, and you’ll see that five lines of our beautiful CSS are applied on the page.

And if we run npm run build you’ll see style.bundle.css4 created in the dist folder, next to js and html files.

4 I use .bundle postfix so you can easier differentiate webpack bundles from the source files.

Show file structure after this step

For the end, we’ll add file-loader. As it’s name suggests it handles files — images, SVGs, fonts, videos or anything else you need.

Let’s create /src/assets/ folder and add Commander Keen5 image in it.

5 I still love Commander Keen games.

Follow the same flow as with CSS files

  • install loader
npm install --save-dev file-loader
  • import image in app.js and render it
import React, { Component } from 'react';
import { render } from 'react-dom';
import '../css/style.css';
import keenImage from '../assets/keen.png'; // Importing image -> ADDED IN THIS STEP
export default class Hello extends Component {
render() {
return (
<div>
Hello from react
        {/* ADDED IN THIS STEP */}
<img src={ keenImage } alt='Commander Keen' />
</div>
);
}
}
render(<Hello />, document.getElementById('app'));
  • update webpack config to handle image assets
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); // -> ADDED IN THIS STEP
// Constant with our paths
const paths = {
DIST: path.resolve(__dirname, 'dist'),
SRC: path.resolve(__dirname, 'src'),
JS: path.resolve(__dirname, 'src/js'),
};
// Webpack configuration
module.exports = {
entry: path.join(paths.JS, 'app.js'),
output: {
path: paths.DIST,
filename: 'app.bundle.js',
},
// Tell webpack to use html plugin
plugins: [
new HtmlWebpackPlugin({
template: path.join(paths.SRC, 'index.html'),
}),
new ExtractTextPlugin('style.bundle.css'), // CSS will be extracted to this bundle file -> ADDED IN THIS STEP
],
// Loaders configuration
// We are telling webpack to use "babel-loader" for .js and .jsx files
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
'babel-loader',
],
},
// CSS loader for CSS files
// Files will get handled by css loader and then passed to the extract text plugin
// which will write it to the file we defined above
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
use: 'css-loader',
}),
},
// File loader for image assets -> ADDED IN THIS STEP
// We'll add only image extensions, but you can things like svgs, fonts and videos
{
test: /\.(png|jpg|gif)$/,
use: [
'file-loader',
],
},
],
},
resolve: {
extensions: ['.js', '.jsx'],
},
};

Restart npm run start and refresh the browser to see the image. npm run build will create image in the dist folder.

Like what you read? Give Tarun Sharma a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.