JavaScript
Article

A Beginner’s Guide to Webpack 2 and Module Bundling

By Mark Brown

This article was peer reviewed by Scott Molinari, Joan Yin and Joyce Echessa. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

A webpack 2 machine that compresses all web elements

Webpack is a module bundler

Webpack has become one of the most important tools for modern web development. Primarily it’s a module bundler for your JavaScript but it can be taught to transform all of your front-end assets like HTML and CSS, even images. It can give you more control over the number of HTTP requests your app is making and allows you to use other flavors of those assets (Jade, Sass & ES6 for example). Webpack also allows you to easily consume packages from npm.

This article is aimed at those who are new to webpack and will cover initial setup and configuration, modules, loaders, plugins, code splitting and hot module replacement. If you find video tutorials helpful I can highly recommend Glen Maddern’s Webpack from First Principles as a starting point to understand what it is that makes webpack special.

To follow along at home you’ll need to have Node.js installed. You can also download the demo app from our Github repo.

Setup

Let’s initialize a new project with npm and install webpack:

mkdir webpack-demo
cd webpack-demo
npm init -y
npm install webpack@beta --save-dev
mkdir src
touch index.html src/app.js webpack.config.js

Edit these files:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hello webpack</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="dist/bundle.js"></script>
  </body>
</html>
// src/app.js
const root = document.querySelector('#root')
root.innerHTML = `<p>Hello webpack.</p>`
// webpack.config.js
const webpack = require('webpack')
const path = require('path')

const config = {
  context: path.resolve(__dirname, 'src'),
  entry: './app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      include: path.resolve(__dirname, 'src'),
      use: [{
        loader: 'babel-loader',
        options: {
          presets: [
            ['es2015', { modules: false }]
          ]
        }
      }]
    }]
  }
}

module.exports = config

The config above is a common starting point, it instructs webpack to compile our entry point src/app.js into our output /dist/bundle.js and all .js files will be transpiled from ES2015 to ES5 with Babel.

To get this running we’re going to need to install three packages, babel-core, the webpack loader babel-loader and the preset babel-preset-es2015 for the flavor of JavaScript we want to write. { modules: false } enables Tree Shaking to remove unused exports from your bundle to bring down the file size.

npm install babel-core babel-loader babel-preset-es2015 --save-dev

Lastly, replace the scripts section of package.json with the following:

"scripts": {
  "start": "webpack --watch",
  "build": "webpack -p"
},

Running npm start from the command line will start webpack in watch mode which will recompile our bundle whenever a .js file is changed in our src directory. The output in the console tells us about the bundles being being created, it’s important to keep an eye on the number of bundles and the size.

Console output of running webpack 2 in watch mode

You should now be able to load index.html in your browser and be greeted with “Hello webpack.”.

open index.html

Open up dist/bundle.js to see what webpack has done, at the top is webpack’s module bootstrapping code and right at the bottom is our module. You may not be colored impressed just yet but if you’ve come this far you can now start authoring ES6 modules and webpack will be able to produce a bundle for production that will work in all browsers.

Stop webpack with Ctrl + C and run npm run build to compile our bundle in production mode.

Notice that the bundle size has come down from 2.61 kB to 585 bytes.
Take another look at dist/bundle.js and you’ll see a big ugly mess of code, our bundle has been minified with UglifyJS, the code will run exactly the same but it’s done with the fewest characters needed.

Modules

Out of the box webpack knows how to consume JavaScript modules in a variety of formats, the most notable two are:

  • ES2015 import statements
  • CommonJS require() statements

We can test this out by installing lodash and importing it from app.js

npm install lodash --save
// src/app.js
import {groupBy} from 'lodash/collection'

const people = [{
  manager: 'Jen',
  name: 'Bob'
}, {
  manager: 'Jen',
  name: 'Sue'
}, {
  manager: 'Bob',
  name: 'Shirley'
}, {
  manager: 'Bob',
  name: 'Terrence'
}]
const managerGroups = groupBy(people, 'manager')

const root = document.querySelector('#root')
root.innerHTML = `<pre>${JSON.stringify(managerGroups, null, 2)}</pre>`

Run npm start to start webpack and refresh index.html, you should see an array of people grouped by manager.

Let’s move the array of people into its own module people.js

// src/people.js
const people = [{
  manager: 'Jen',
  name: 'Bob'
}, {
  manager: 'Jen',
  name: 'Sue'
}, {
  manager: 'Bob',
  name: 'Shirley'
}, {
  manager: 'Bob',
  name: 'Terrence'
}]

export default people

We can simply import it from app.js with a relative path.

// src/app.js
import {groupBy} from 'lodash/collection'
import people from './people'

const managerGroups = groupBy(people, 'manager')

const root = document.querySelector('#root')
root.innerHTML = `<pre>${JSON.stringify(managerGroups, null, 2)}</pre>`

Note: Imports without a relative path like 'lodash/collection' are modules from npm installed to /node_modules, your own modules will always need a relative path like './people', this is how you can tell them apart.

Loaders

We’ve already been introduced to babel-loader, one of many loaders that you can configure to tell webpack what to do when it encounters imports for different file types. You can chain loaders together into a series of transforms, a good way to see how this works is by importing Sass from our JavaScript.

Sass

This transformation involves three separate loaders and the node-sass library:

npm install css-loader style-loader sass-loader node-sass --save-dev

Add a new rule to our config file for .scss files.

// webpack.config.js
rules: [{
  test: /\.scss$/,
  use: [
    'style-loader',
    'css-loader',
    'sass-loader'
  ]
}, {
  // ...
}]

Note: Whenever you change any of loading rules in webpack.config.js you’ll need to restart the build with Ctrl + C and npm start.

The array of loaders are processed in reverse order:

  • sass-loader transforms Sass into CSS.
  • css-loader parses the CSS into JavaScript and resolves any dependencies.
  • style-loader outputs our CSS into a <style> tag in the document.

You can think of these as function calls, the output of one loader feeds as input into the next.

styleLoader(cssLoader(sassLoader('source')))

Let’s add a Sass source file:

/* src/style.scss */
$bluegrey: #2B3A42;

pre {
  padding: 20px;
  background: $bluegrey;
  color: #dedede;
  text-shadow: 0 1px 1px rgba(#000, .5);
}

You can now require Sass directly from your JavaScript, import it from the top of app.js.

// src/app.js
import './style.scss'

// ...

Reload index.html and you should see some styling.

CSS in JS

We just imported a Sass file from our JavaScript, as a module.

Open up dist/bundle.js and search for “pre {“. Indeed, our Sass has been compiled to a string of CSS and saved as a module within our bundle. When we import this module in our JavaScript, style-loader outputs that string into an embedded <style> tag.

I know what you’re thinking. Why?

I won’t delve too far into this topic here, but here are a few reasons to consider:

  • A JavaScript component you may want to include in your project may depend on other assets to function properly (HTML, CSS, Images, SVG), if these can all be bundled together it is far easier to import and use.
  • Dead code elimination: When a JS component is no longer imported by your code, the CSS will no longer be imported either. The bundle produced will only ever contain code that does something.
  • CSS Modules: The global namespace of CSS makes it very difficult to be confident that a change to your CSS will not have any side effects. CSS modules change this by making CSS local by default and exposing unique class names that you can reference in your JavaScript.
  • Bring down the number of HTTP requests by bundling / splitting code in clever ways.

Images

The last example of loaders we’ll look at is the handling of images with url-loader.

In a standard HTML document images are fetched when the browser encounters an <img> tag or an element with a background-image property. With webpack you can optimize this in the case of small images by storing the source of the images as strings inside your JavaScript. By doing this you preload them and the browser won’t have to fetch them with separate requests later.

npm install file-loader url-loader --save-dev

Add one more rule for loading images:

// webpack.config.js
rules: [{
  test: /\.(png|jpg)$/,
  use: [{
    loader: 'url-loader',
    options: { limit: 10000 } // Convert images < 10k to base64 strings
  }]
}, {
  // ...
}]

Restart the build with Ctrl + C and npm start.

Download a test image with this command:

curl https://raw.githubusercontent.com/sitepoint-editors/webpack-demo/master/src/code.png --output src/code.png

You can now import the image source from the top of app.js:

// src/app.js
import codeURL from './code.png'
const img = document.createElement('img')
img.src = codeURL
img.style.backgroundColor = "#2B3A42"
img.style.padding = "20px"
img.width = 32
document.body.appendChild(img)

// ...

This will include an image where the src attribute contains a data URI of the image itself.

<img src="data:image/png;base64,iVBO..." style="background: #2B3A42; padding: 20px" width="32">

Also, thanks to css-loader images referenced with url() also run through url-loader to inline them directly in the CSS.

/* src/style.scss */
pre {
  background: $bluegrey url('code.png') no-repeat center center / 32px 32px;
}

Compiles to this

pre {
    background: #2b3a42 url("data:image/png;base64,iVBO...") no-repeat scroll center center / 32px 32px;
}

Modules to Static Assets

You should be able to see now how loaders help to build up a tree of dependencies amongst your assets, this is what the image on the webpack homepage is demonstrating.

JS requires SCSS requires CSS requires PNG

Though JavaScript is the entry point, webpack appreciates that your other asset types like HTML, CSS, and SVG each have dependencies of their own which should be considered as part of the build process.

Plugins

We’ve seen one example of a built in webpack plugin already, webpack -p which is called from our npm run build script uses the UglifyJsPlugin which ships with webpack to minify our bundle for production.

While loaders operate transforms on single files plugins operate across larger chunks of code.

Common code

The commons-chunk-plugin is another core plugin that ships with webpack that can be used to create a separate module with shared code across multiple entry points. Until now we’ve been using a single entry point and single output bundle. There are many real-world scenarios where you’ll benefit from splitting this into multiple entry and output files.

If you have two distinct areas of your application that both share modules, for example app.js for a public facing app and admin.js for an administration area you can create separate entry points for them like so:

// webpack.config.js
const webpack = require('webpack')
const path = require('path')

const extractCommons = new webpack.optimize.CommonsChunkPlugin({
  name: 'commons',
  filename: 'commons.js'
})

const config = {
  context: path.resolve(__dirname, 'src'),
  entry: {
    app: './app.js',
    admin: './admin.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].bundle.js'
  },
  module: {
    // ...
  },
  plugins: [
    extractCommons
  ]
}

module.exports = config

Notice the change to the output.filename which now includes [name], this is replaced with the chunk name so we can expect two output bundles from this configuration: app.bundle.js and admin.bundle.js for our two entry points.

The commonschunk plugin generates a third file commons.js which includes shared modules from our entry points.

// src/app.js
import './style.scss'
import {groupBy} from 'lodash/collection'
import people from './people'

const managerGroups = groupBy(people, 'manager')

const root = document.querySelector('#root')
root.innerHTML = `<pre>${JSON.stringify(managerGroups, null, 2)}</pre>`
// src/admin.js
import people from './people'

const root = document.querySelector('#root')
root.innerHTML = `<p>There are ${people.length} people.</p>`

These entry points would output the following files:

  • app.bundle.js includes the style and lodash/collection modules
  • admin.bundle.js doesn’t include extra modules
  • commons.js includes our people module

We could then include the commons chunk in both areas:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hello webpack</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="dist/commons.js"></script>
    <script src="dist/app.bundle.js"></script>
  </body>
</html>
<!-- admin.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hello webpack</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="dist/commons.js"></script>
    <script src="dist/admin.bundle.js"></script>
  </body>
</html>

Try loading index.html and admin.html in the browser to see them run with the automatically created commons chunk.

Extracting CSS

Another popular plugin is the extract-text-webpack-plugin which can be used to extract modules into their own output files.

Below we’ll modify our .scss rule to compile our Sass, load the CSS, then extract each into its own CSS bundle, thus removing it from our JavaScript bundle.

npm install extract-text-webpack-plugin@2.0.0-beta.4 --save-dev
// webpack.config.js
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const extractCSS = new ExtractTextPlugin('[name].bundle.css')

const config = {
  // ...
  module: {
    rules: [{
      test: /\.scss$/,
      loader: extractCSS.extract(['css-loader','sass-loader'])
    }, {
      // ...
    }]
  },
  plugins: [
    extractCSS,
    // ...
  ]
}

Restart webpack and you should see a new bundle app.bundle.css which you can link to directly, as usual.

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hello webpack</title>
    <link rel="stylesheet" href="dist/app.bundle.css">
  </head>
  <body>
    <div id="root"></div>
    <script src="dist/commons.js"></script>
    <script src="dist/app.bundle.js"></script>
  </body>
</html>

Refresh the page to confirm our CSS has been compiled and moved from app.bundle.js to app.bundle.css. Success!

Code Splitting

We’ve looked at a few ways to split our code already:

  • Manually creating separate entry points
  • Automatic splitting of shared code into a commons chunk
  • Extracting chunks out of our compiled bundle with extract-text-webpack-plugin

Another way to split our bundle is with System.import or require.ensure. By wrapping sections of code in these functions you create a chunk to be loaded on demand at run time. This can significantly improve load time performance by not sending everything to the client at the start. System.import takes the module name as an argument and returns a Promise. require.ensure takes a list of dependencies, a callback and an optional name for the chunk.

If one section of your app has heavy dependencies that the rest of the app doesn’t need, it’s a good case for splitting into its own bundle. We can demonstrate this by adding a new module named dashboard.js that requires d3.

npm install d3 --save
// src/dashboard.js
import * as d3 from 'd3'

console.log('Loaded!', d3)

export const draw = () => {
  console.log('Draw!')
}

Import dashboard.js from the bottom of app.js

// ...

const routes = {
  dashboard: () => {
    System.import('./dashboard').then((dashboard) => {
      dashboard.draw()
    }).catch((err) => {
      console.log("Chunk loading failed")
    })
  }
}

// demo async loading with a timeout
setTimeout(routes.dashboard, 1000)

Because we’ve added asynchronous loading of modules, we need an output.publicPath property in our config so that webpack knows where to fetch them.

// webpack.config.js

const config = {
  // ...
  output: {
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/dist/',
    filename: '[name].bundle.js'
  },
  // ...
}

Restart the build and you’ll see a mysterious new bundle 0.bundle.js

Webpack 2 outputs a mysterious new bundle

Notice how webpack keeps you honest by highlighting the [big] bundles for you to keep an eye on.

This 0.bundle.js will be fetched on demand with a JSONP request, so loading the file directly from the file system isn’t going to cut it anymore. We’ll need to run a server, any server will do.

python -m SimpleHTTPServer 8001

Open http://localhost:8001/

One second after loading you should see a GET request for our dynamically generated bundle /dist/0.bundle.js and “Loaded!” logged to the console. Success!

Webpack Dev Server

Live reloading can really improve the developer experience by refreshing automatically whenever files are changed. Simply install it and start it with webpack-dev-server and you’re off to the races.

npm install webpack-dev-server@2.2.0-rc.0 --save-dev

Modify the start script in package.json

"start": "webpack-dev-server --inline",

Run npm start to start the server and open http://localhost:8080 in your browser.

Try it out by changing any of the src files, e.g. change a name in people.js or a style in style.scss to see it transform before your very eyes.

Hot Module Replacement

If you’re impressed by live reloading, hot module replacement (HMR) will knock your socks off.

It’s the year 2017, chances are that you’ve already worked on single-page apps with global state. During development you’ll be making a lot of small changes to components and you want to see these reflected in a real browser where you see the output and interact with it. By refreshing the page manually or with live reload your global state is blown away and you need to start from scratch. Hot module replacement has forever changed this.

In the developer workflow of your dreams you can make changes to a module and it is compiled and swapped out at run time without refreshing the browser (blowing away the local state) or touching other modules. There are still times when a manual refresh is required, but HMR can still save you a massive amount of time and it feels like the future.

Make one final edit to the start script in package.json.

"start": "webpack-dev-server --inline --hot",

At the top of app.js tell webpack to accept hot reloading of this module and any of its dependencies.

if (module.hot) {
  module.hot.accept()
}

// ...

Note: webpack-dev-server --hot sets module.hot to true which includes this for development only. When building in production mode module.hot is set to false so these are stripped out of the bundle.

Add NamedModulesPlugin to the list of plugins in webpack.config.js to improve logging in the console.

plugins: [
  new webpack.NamedModulesPlugin(),
  // ...
]

Lastly, add an <input> element to the page where we can add some text to confirm a full page refresh doesn’t occur when we make a change to our module.

<body>
  <input />
  <div id="root"></div>
  ...

Restart the server with npm start and behold hot reloading!

To try this out, enter “HMR Rules” in the input and then change a name in people.js to see it swapped out without refreshing the page and losing the state of the input.

This is a simple example but hopefully you can see how wildly useful this is. It’s especially true with component based development like React where you have a lot of “dumb” components separated from their state, components can be swapped out and re-rendered without losing state so you get an instant feedback loop.

Hot Reloading CSS

Change the background color of the <pre> element in style.scss and you’ll notice that it’s not being replaced with HMR.

pre {
  background: red;
}

It turns out that HMR of CSS comes for free when you’re using style-loader, you don’t need to do anything special. We just broke this link in the chain by extracting the CSS modules out into external CSS files which can’t be replaced.

If we revert our Sass rule to its original state and remove extractCSS from the list of plugins, you’ll be able see hot reloading of your Sass too.

{
  test: /\.scss$/,
  loader: ['style-loader', 'css-loader','sass-loader']
}

HTTP/2

One of the primary benefits of using a module bundler like webpack is that it can help you improve performance by giving you control over how the assets are built and then fetched on the client. It has been considered best practice for years to concatenate files to reduce the number of requests that need to made on the client. This is still valid but HTTP/2 now allows multiple files to be delivered in a single request so concatenation isn’t a silver bullet anymore. Your app may actually benefit from having many small files individually cached, the client could then fetch a single changed module rather than having to fetch an entire bundle again with mostly the same contents.

The creator of Webpack Tobias Koppers has written an informative post explaining why bundling is still important, even in the HTTP/2 era.

Read more about this over at webpack & HTTP/2.

Over to You

I sure hope you have found this introduction to webpack 2 helpful and are able to start using it to great effect. It can take a little time to wrap your head around webpack’s configuration, loaders and plugins but learning how this tool works will pay off.

The documentation is still being worked on but there’s a handy Migrating from v1 to v2 guide if you want to shift an existing Webpack 1 project across to the new hotness.

Is webpack your module bundler of choice? Let me know in the comments below.

More:
  • markbrown4

    Hey P.K, that’s true, Webpack and bundling is an advanced topic, this is a tutorial for those beginning Webpack. Watch the video I linked to at the top if you’re looking to understand the motivation behind Webpack. https://www.youtube.com/watch?v=WQue1AN93YU

    One thing it will do for you is take away the need for tedious manual minification through online minifiers, that can all be abstracted away behind a simple script “npm run build”.

    Not every site requires an advanced tool like Webpack though, it’s only when you have a substantial amount of JS that you benefit from breaking things up into modules. As more and more logic is moves to the client tools like Webpack become necessary.

  • Micha

    I’m getting an error while trying to load the png file: Unexpected character ‘�’
    I follow the tutorial and all packages has been loaded.
    Any ideas?

    • markbrown4

      That will occur if you don’t have the url-loader in webpack.config.js – When you add a rule to the config you need to restart the build with Ctrl + C and npm start.

  • Dave Parsons

    Skipping semicolons in Javascript is kind of a bad habit to promote in a tutorial. Otherwise a good walk through.

    • markbrown4

      I disagree :) See https://www.sitepoint.com/why-use-javascript-style-guide/ for more detail.

      The code above runs exactly the same with or without semicolons.

      • Dave Parsons

        Fair enough. The code follows your style guide. :)

        However, there are some double quotes in app.js. Webpack Part 2, using preloaders to run jslint?

  • Dave Parsons

    Ok, an actual Webpack question. In the Extracting CSS section, it wasn’t initially clear to me why CSS needs to be extracted. Why isn’t Sass just converted to CSS and written out? I’m guessing Webpack is very JS centric, so everything is processed into or out of JS bundles? So the Sass will be converted to CSS, converted to JS, and then extracted as a separate file?

    • markbrown4

      It’s a good question.

      First up, importing css from js is not a requirement for using webpack. It’s a feature you opt into if you see value in some of the reasons I outlined like dead code elimination, image inlining or CSS Modules.

      If you do decide that you want to give css in js a try you have two usual options for where it’s compiled to, either in a JS bundle and added to the page at runtime or extracted to CSS and linked to from the HTML. Extracting to CSS may give you a better end result as the styles can be loaded by the browser before the JS is downloaded and executed.

      Webpack gives you a lot of options for how your assets can be compiled but it’s not prescriptive, it’s up to you if you leave the CSS in JS, extract it from JS, or keep it out of the JS build entirely.

      When CSS is parsed by css-loader it also adds its dependencies(images) to the tree of things that webpack knows about.

    • Im So Meta Even This Acronym

      Your point is totally valid. It’s a design flaw. The creators of webpack wanted a JS module bundler, but then everybody started to throw in stuff to make it more hip and we have now this multiple workarounds to do simple things like this.

      Of course it’s a hassle to run multiple build systems to have total control so people started to invent “ingenious” ways to do things that should be simple configuration option.

      • markbrown4

        Nope, there has been a steady progression towards CSS in JS and it’s not going away.

        As for comment about simple configuration, it can be this simple to get Sass, CSS and Image loading working in webpack:


        rules: [{
        test: /.scss$/,
        use: ['style-loader', 'css-loader', 'sass-loader']
        }, {
        test: /.(png|jpg)$/,
        use: [{
        loader: 'url-loader',
        options: { limit: 10000 }
        }]
        }]

        • Im So Meta Even This Acronym

          My point still stands reflecting on OP’s problem that it’s poor design by the Webpack team.

          • markbrown4

            Importing CSS from JS? it’s a feature that many people are experimenting with at the moment. See https://vuejs.org/v2/guide/single-file-components.html and https://github.com/zeit/styled-jsx

          • Im So Meta Even This Acronym

            No, see OP’s comment again. It’s not possible to output a compiled CSS by default from SASS without hackish soltions like extract-text-webpack-plugin. It’s a few lines with gulp for example or can be done with pure NPM scripts.

            BTW I find importing CSS to JS a very bad idea. CSS is fine as is. SASS solves most of the problems. A sloppy team won’t be saved by extreme CSS scoping.

          • markbrown4

            I read it. Without going through JS none of the benefits I listed are possible.

          • Im So Meta Even This Acronym

            You are ignoring my point :) Why can’t the webpack config have a, say “pipe” key in the JSON that outputs whatever’s loaded there? And I mean by default, documented way, not as a 3rd party plugin.

            I know, I know. This is FOSS, I should go implement it :)))

  • markbrown4

    Thanks George, well spotted, that’s an error in the article. If styles are not included by any of the entry points it will not be proccessed by webpack. I’ll correct it.

  • markbrown4

    Ah, that could be a path issue on windows.. Are these the steps you took to see the error?


    git clone https://github.com/sitepoint-editors/webpack-demo
    cd webpack-demo
    npm install
    npm start

    If it’s a path issue you’ll need to require the ‘path’ module in “webpack.config.js” and edit any of the places that reference “__dirname”


    var path = require('path')

    module.exports = {
    output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
    }

  • Benjamin Russell

    Hi great tutorial, should webpack-dev-server live reload after a change?, currently I have to refresh the page to see the change, is it built in or do I have to add an additional plugin?

    • markbrown4

      Thanks! yep “webpack-dev-server –inline” should have live reloading at http://localhost:8080/

  • mimmo falco

    Hi. That’s a great and detailed basic guide! Thanks a lot, I loved it.
    I have one question: could you also explain with some example how to use “vendors” with webpack 2? That would be really awesome! Cheers

  • markbrown4

    Ah, the demo repo has a version of the code with HMR and the CSS is extracted so the css won’t be refreshed automatically. I’d expect changes to the JS to be reloaded though.

    Look in the “Hot Reloading CSS” for instructions on changing that rule and removing the plugin to see HMR of the styles too.

    Best to follow the steps from the top without the demo repo if you want to understand each of the components.

    • Benjamin Russell

      yes the JS is reloading as expected, apologies should have mentioned that, cool I’ll look into that, much appreciated for getting back so quickly.

  • Great guide. I recently started to integrate Webpack (1) into a project I’m working on and I’m happy to see I’ve implemented most of the above correctly. One thing I struggled with was that I have a back-end running on a local server and I could not get hot module replacement to work (or any change detection). I kind of solved it by instead using Browser-sync, but it’s still not HMR, it just refreshes the page. For now it’s not a really big deal, but it would be nice to know how I can get HMR running when my site is already on a local server running on a ip address.

    Maybe this question is way too specific for this guide, but there must be other people who use a back-end, are dependent on it, and want to use webpack .

    • markbrown4

      Thanks Neils, I haven’t tried HMR anywhere other than webpack-dev-server so can’t really help out there.. It sounds feasible to run two servers locally though and still have webpack-dev-server there for HMR even though it’s not serving the HTML.

  • markbrown4

    Hey, thanks for reading. If you follow along step by step I do have the install instructions for that package before adding it to the config. https://www.sitepoint.com/beginners-guide-to-webpack-2-and-module-bundling/#extractingcss so you shouldn’t see errors.

    I can’t see your error(404) but the other issue was resolved by switching to using path.resolve() for Windows support.

    • Euclides Fernandes

      Hey…
      If you take a look at the tutorial you’ll notice that from the step you write the “web.pack.config.js” and include this line “const ExtractTextPlugin = require(‘extract-text-webpack-plugin’)” until the step we run “npm start” for the first time, there isn’t any instruction to install the package. And sorry for the wrong link. Try this one: http://prntscr.com/e5nt4t . I’m already using “path.resolve()” as you explained to Veronica.

      • markbrown4

        Oh, this looks a bad edit that was made. I’ll have it fixed, thanks for raising..

  • Hi there,
    first off very good tutorial thx :)
    Just to point out
    with this
    “`
    img.style = “background: #2B3A42; padding: 20px”
    “`
    Safari doesn’t work and it could be frustrating
    better

    “`
    img.style.backgroundColor = “#2B3A42”;
    img.style.padding = “20px”;
    “`

    • markbrown4

      Ah, right you are. Thanks for raising. Will update the article.

  • Today there is no need to add @beta or @rc0 when install webpack or webpack-dev-server with npm install. Webpack 2.2 is installed by default if version isn’t provided.

  • very good explation!

  • Pier Luigi Doganieri

    Great introduction to webpack 2. i’m using your guide to upgrade from webpack 1. but i got some issues when trying to load fonts. specifically woff, woff2 of font-awesome. i use this configuration in my webpack1 boilerplate and it is working as expected but broke in webpack 2

    … scss, js, jsx ,images and other fonts loader…
    {
    test: /.woff(2)?(?v=[0-9].[0-9].[0-9])?$/,
    loader: “url-loader?limit=10000&mimetype=application/font-woff&name=fonts/[name].[ext]”
    }

    and then in my main.js
    import ‘font-awesome/scss/font-awesome.scss’;

    and in both cases im using the same module “font-awesome”: “^4.7.0”

    it is posible you continue the guide to add some cases using fonts? or can anyone explain me what am i doing wrong?
    here i created a github repo if anyone want to take a look https://github.com/luigi055/webpack2-boilerplate
    Thanks

    • markbrown4

      Try using the new syntax for loader options like this:


      rules: [{
      test: /.woff(2)?(?v=[0-9].[0-9].[0-9])?$/,
      use: [{
      loader: 'url-loader',
      options: {
      limit: 10000,
      mimetype: 'application/font-woff',
      name: 'fonts/[name].[ext]'
      }
      }]
      }

      • Pier Luigi Doganieri

        Thanks bro! i fixed this already. i use a similar syntax. you can check that in my boilerplate :)!

  • Per Gustav Eskilson

    Please hightlight that publicPath: ‘/dist/’ must be set in the output section of file webpack.config.js for webpack-dev-server –inline to work.

    This is mentioned in one section before webpack-dev-server, but the latter section is far more important for most readers I believe.

    In file webpack.config.js

    output: {
    path: path.resolve(__dirname, ‘dist’),
    publicPath: ‘/dist/’, // IMPORTANT: Must be here for webpack-dev-server
    filename: ‘[name].bundle.js’
    },

    • RichB Zone

      That’s just saved me such a headache. Thank you Gustav

  • Keith Gunn

    In other examples ive seen you pass in a component to hot.accept that calls render again. What effect does not passing this in mean

    • markbrown4

      The new docs for Webpack 2 only have a React example like you describe https://webpack.js.org/guides/hmr-react/#code The previous docs have more info on the HMR API https://webpack.github.io/docs/hot-module-replacement.html.

      hot.accept() without any arguments simply re-executes the changed script/module. If you want to do something after loading the module like calling render you can do it in the callback.


      if (module.hot) {
      module.hot.accept('./components/App', () => {
      render(App)
      });
      }

      There’s also module.hot.dispose(fn) for tear down and module.hot.decline(fn) if you’re unable to handle the replacement for whatever reason.

Recommended
Sponsors
Get the latest in JavaScript, once a week, for free.