Code Oil
Published in

Code Oil

Why webpack-dev-server Live-Reload Is Not Working

Firstly, why would anybody use webpack-dev-server?

One of the goals of webpack-dev-server is to shorten the feedback loop between coding and seeing the result. This increases productivity.

This means that any code change, appears automagically and instantly on the browser.

Here is what webpack-dev server actually does:

  • Serves static html files, which may contain javascript that makes them dynamic
  • Automatically re-generate a bundle for your front-end resources (javascript, css, images, etc.) upon change and serve it
  • Automagically refreshes your browser so that your code changes appears immediately

webpack-dev-server is highly customizable, which is great if you fully grasp the product, but customizability is a huge stumbling block for most beginners.

This article documents the most common pitfalls when setting up ‘live-reload’ on webpack-dev-server.

If you harbor any of these burning questions, jump to the relevant sections to get the answers else skip to the ‘Conclusions’ where I summarize the top confusing things about webpack-dev-server and ‘live-reload’.

Burning questions 🔥 🔥 🔥 🔥 :

  1. Why can’t I find example webpack-dev-server configuration to setup live-reload?
  2. Where should I put the javascript bundle generated by Webpack CLI so that webpack-dev-server can find it?
  3. Why is Hot-Module Replacement (HMR) not working?
  4. Why is ‘live-reloading’ not happening when I modify my front-end code?
  5. When I modify javascript files, I do not see changes to bundle.js in the local filesystem, nor bundle.js file pointed to by html page <script> tag. Manually reloading the web page does not reflect code change.
  6. When I modify javascript files, I have to manually run Webpack CLI to see changes to bundle.js in the local filesystem, and bundle.js file pointed to by html page <script> tag, but browser is not auto-reloading. Manually reloading the web page picks up code change.
  7. webpack-dev-server works great but how can I make it work in production?

webpack-dev-server Live-Reload Burning Questions

Q1: Why can’t I find example webpack-dev-server configuration to set up ‘live-reload’?

You can now!

This webpack-dev-server ‘live-reload’ configuration should work for everyone. Read the comments carefully to understand each configuration parameter.

NOTE: We show examples of absolute (entry/app) and relative path (output/path, contentBase) configuration. Relative path is relative to where the Webpack config file is located.

Thanks to Daniel Sobota’s question, I’ll also show what the sample directory structure looks like:

module.exports = {
target: "web",
// webpack-dev-server will monitor the code dependency
// of these entry points, and re-create the bundle
// when changes are detected. In this example, the main
// javascript is main.js, and it points to
// other code dependencies
entry: {
app: ["./app/javascripts/packs/main.js"]
},
// This specifies where javascript bundle is created when
// webpack CLI is run. However, webpack-dev-server is only
// concerned with the 'filename' parameter.
// webpack-dev-server generates the bundle with the 'filename' in
// memory. It never creates an actual file in the 'path' specified
// unlike the webpack CLI.
output: {
path: path.resolve(__dirname, "../build/assets"),
filename: "bundle.js",
},
// webpack-dev-server configuration
devServer: {
// Can be omitted unless you are using 'docker'
host: '0.0.0.0',
// This is where webpack-dev-server serves your bundle
// which is created in memory.
// To use the in-memory bundle,
// your <script> 'src' should point to the bundle
// prefixed with the 'publicPath', e.g.:
// <script src='http://localhost:9001/assets/bundle.js'>
// </script>
publicPath: '/assets/',
// The local filesystem directory where static html files
// should be placed.
// Put your main static html page containing the <script> tag
// here to enjoy 'live-reloading'
// E.g., if 'contentBase' is '../views', you can
// put 'index.html' in '../views/main/index.html', and
// it will be available at the url:
// https://localhost:9001/main/index.html
contentBase: path.resolve(__dirname, "../views"),
// 'Live-reloading' happens when you make changes to code
// dependency pointed to by 'entry' parameter explained earlier.
// To make live-reloading happen even when changes are made
// to the static html pages in 'contentBase', add
// 'watchContentBase'
watchContentBase: true,
compress: true,
port: 9001
},
... other webpack configuration ...}

To start webpack-dev server with webpack-dev-server if you have installed it globally or node_modules/webpack-dev-server/bin/webpack-dev-server.js if you have installed it as a development dependency. Upon start, you should see.

Project is running at http://0.0.0.0:9001/webpack output is served from /assets/Content not from webpack is served from /app/nodejs/views

Carefully note:

  • The webpack-dev-server project url: http://0.0.0.0:9001
  • The ‘webpack output’ (publicPath), which should be appended to the webpack-dev-pack project url to access your bundle: /assets/. E.g. http://0.0.0.0:9001/assets/bundle.js
  • The ‘Content not from webpack’ , is where static html files will be served from. E.g., http://0.0.0.0:9001/index.html, will make webpack-dev-server to search for ‘index.html’ from the local filesystem path '/app/nodejs/views’ (contentBase)

Q2: Where should I put the javascript bundle generated by Webpack CLI so that webpack-dev-server can find it?

You don’t. A javascript bundle whose name is derived from output/filename parameter (see Webpack configuration below) is generated in memory by webpack-dev-server from code dependency pointed to by entry parameter, and served from url path specified bypublicPath. E.g., in the following configuration, the in-memory bundle generated from code in ./app/javascripts/packs/main.js is served at ‘http://localhost/assets/bundle.js’ (read the comment for details):

module.exports = {  // webpack-dev-server creates a bundle in memory
// from the entrypoint code specified by 'entry'
entry: {
app: ["./app/javascripts/packs/main.js"]
},
// IMPORTANT: webpack-dev-server only cares about the
// output 'filename', which it uses as the name of
// the bundle it creates in memory
output: {
path: path.resolve(__dirname, "../build/assets"),
filename: "bundle.js",
},
// webpack-dev-server configuration
devServer: {
// This is where webpack-dev-server serves your bundle
// which is created in memory. In this example it will be:
// http://localhost/assets/bundle.js
publicPath: '/assets/',
.. other devServer configuration ..},... other webpack configuration ...}

Q3: Why is Hot-Module-Replacement (HMR) not working?

Be clear whether you want live-reloading or HMR. Confusion arises when all you want is ‘live-reloading’ but end up trying to setup HMR.

‘Live-reloading’ is a simple but effective way to shorten code-result feedback loop; the browser is refreshed immediately each time there is a code change.

HMR is a more advanced feature that allows code changes to be reloaded in the browser without page refresh. This has the benefit that the javascript front-end code can continue execution with existing state (variables, etc.). E.g., if you notice some tweaks are needed in the last phase of your UI testing, such as the checkout page, you can make the tweak, watch it take effect immediately, and continue testing, unlike a page refresh, which will reload the page, and require you to redo the actions to get to that state before you can see the effect of your code changes.

HMR, although more desirable, also has more requirements, e.g., your javascript code has to use modules that supports HMR, e.g., style-loader, react-hot-loader, etc. For HMR setup details, checkout this post.

Q4: Live-reloading is not happening when front-end code is modified?

There are two parts to front-end code:

  • Front-end javascript that makes our web pages dynamic and interactive
  • Pure static html pages and static html pages that are shell pages embellished by the front-end javascript, i.e., they contain i.e., <script src=’bundle.js'></script>`

Be clear which you are modifying. This section explains why shell html pages modification are not ‘live-reloaded’. If your javascript modification is not ‘live-reloading’, please refer to other relevant sections.

By default, webpack-dev-server only monitors code changes in any code dependency from the starting point specified by entry parameter.

Modifying the shell html pages will not trigger a live-reload unless you configure webpack-dev-server with watchContentBase to watch where your shell html pages are with the following configuration (read the comment for details):

module.exports = {  // webpack-dev-server configuration
devServer: {
// This is where webpack-dev-server serves your static
// pages. In this example configuration, put your shell
// page index.html in './views' directory, and it becomes
// accessible from:
// http://localhost/index.html
contentBase: './views/',
// Make webpack-dev-server live-reload when your
// shell page changes
watchContentBase: true,
.. other devServer configuration .. },... other webpack configuration ...}

Q5: When I modify javascript files, I do not see changes to bundle.js in the local filesystem, nor bundle.js file pointed to by html page <script> tag. Manually reloading the web page does not reflect code change.

Problem

Your html page <script> tag is not pointing to webpack-dev-server in-memory generated ‘bundle.js’, but rather to a static ‘bundle.js’ exposed by contentBase and served statically from your local filesystem.

Fix

Makeyour <script> tag point to the webpack-dev-server in-memory ‘bundle.js’, which is served from the url path specified by publicPath.

Explanation

webpack-dev-server creates ‘bundle.js’ in memory from code pointed to by entry parameter, and serves it from the url path specified by publicPath parameter. webpack-dev-server does not create any ‘bundle.js’ file in your local filesystem.

The following example Webpack configuration means that any changes in code dependency of ./app/javascripts/packs/main.jswill trigger a code rebuild and the updated in-memory ‘bundle.js’ is available at http://localhost/assets/bundle.js

module.exports = {  // Any changes in code dependency pointed to by 'entry'
// will trigger webpack-dev-server to create a new
// bundle in memory
entry: {
app: ["./app/javascripts/packs/main.js"]
},
// webpack-dev-server configuration
devServer: {
// This is where webpack-dev-server serves your bundle
// which is created in memory. In this example it will be:
// http://localhost/assets/bundle.js
publicPath: '/assets/',
.. other devServer configuration .. }, ... other webpack configuration ...}

Your html page <script> tag is likely not pointing to the webpack-dev-server in-memory ‘bundle.js’. It may be pointing to ‘bundle.js’ that you are serving out of contentBase, which is not updated by webpack-dev-server, thus you never see the code changes.

Q6: When I modify javascript files, I have to manually run Webpack CLI to see changes to bundle.js in the local filesystem, and bundle.js file pointed to by html page <script> tag, but browser is not auto-reloading. Manually reloading the web page picks up code change.

Problem

Your html page <script> tag is not pointing to webpack-dev-server in-memory generated ‘bundle.js’, but rather to a static ‘bundle.js’ exposed by contentBase and served statically from your local filesystem.

Fix

Make your <script> tag point to the webpack-dev-server in-memory bundle, which is served from the url path specified by publicPath.

Explanation

Your Webpack configuration may have the output pointing to the same directory as the contentBase, and your <script> tag is then referencing the ‘bundle.js’ in contentBase. Thus each time you run Webpack CLI after code change the ‘bundle.js’ is updated, and made available over webpack-dev-server through contentBase. However, webpack-dev-server is not monitoring changes to your contentBase so it does not force a browser refresh.

Setting watchContentBase to true in your Webpack configuration, will ‘fix’ this issue, but in the wrong way. When using webpack-dev-server, you should never have to run Webpack CLI to make ‘live-reload’ work.

When you make code changes to any code dependency pointed to by entry webpack-dev-server re-generates ‘bundle.js’ in-memory, and makes it available over publicPath.

The following example Webpack configuration means that any changes in code dependency of ./app/javascripts/packs/main.jswill trigger a live-reload and the updated in-memory ‘bundle.js’ is available at http://localhost/assets/bundle.js

module.exports = {// Any changes in code dependency pointed to by 'entry'
// will trigger webpack-dev-server to create a new
// bundle.js in memory
entry: {
app: ["./app/javascripts/packs/main.js"]
},
// webpack-dev-server configuration
devServer: {
// This is where webpack-dev-server serves your bundle.js
// which is created in memory
publicPath: '/assets/',
.. other devServer configuration ..},... other webpack configuration ...}

The right fix is to make your html page <script> tag point to the webpack-dev-server in-memory ‘bundle.js’.

Q7: webpack-dev-server works great but how can I get it to work in production

You should not use webpack-dev-server in production. You should run Webpack CLI to generate a bundle file, which will be placed in the directory with name specified by the output parameter in the Webpack configuration, and in your production build process copy the bundle file into the your production server’s assets directory so that your code can reference it.

Conclusions

The most confusing things about webpack-dev-server ‘live-reload’ are:

  • webpack-dev-server by default only monitors changes to code dependency pointed to by entry parameter in Webpack configuration
  • To make webpack-dev-server monitor changes to static files, set watchContentBase to true
  • Upon code changes, webpack-dev-server generates the bundle file in-memory, so to enjoy ‘live-reload’, you need to ensure that your html page <script> tags are pointing to the in-memory bundle, which is served at url pointed to by publicPath
  • Static files are served from url pointed to by contentBase
  • output points to the directory and filename of the bundle generated when Webpack CLI is run. The file generated in this directory is never used by webpack-dev-server. webpack-dev-server does refer to output to obtain the name of the bundle it creates in memory.

If you are interested to use webpack to bundle code for both front-end and back-end (nodejs) code, check out this article.

--

--

--

Attempts to find better ways to help myself, and others, understand software technology

Recommended from Medium

Enhance Your Front-End State Management with View Models — Part 2

Authentication with Auth0 — Part #7 of 11

Bubble Sort

Promise.allpocalypse

Learning React with Ben Awad — 05

Variables and Data types in JavaScript -Fundamentals of JavaScript (Part II)

Let’s learn about Koa.js.

Leverage the Webpacker gem to incorporate Mirador Viewer into GeoBlacklight

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Soon Hin Khor, Ph.D.

Soon Hin Khor, Ph.D.

Use tech to make the world more caring, and responsible. Nat. Univ. Singapore, Carnegie Mellon Univ, Univ. of Tokyo. IBM, 500Startups & Y-Combinator companies

More from Medium

(Forge) Bridge over troubled water

Demystifying Web Workers

Working of web worker

The Babel Toolchain

Callback Functions: First they’re sour, then they’re sweet.