How to allow for webpack-dev-server to allow entry points from react-router

I'm creating an app that uses webpack-dev-server in development alongside react-router.

It seems that webpack-dev-server is built around the assumption that you will have a public entry point at one place (i.e. "/"), whereas react-router allows for an unlimited amount of entry points.

I want the benefits of the webpack-dev-server, especially the hot reloading feature that is great for productivity, but I still want to be able to load routes set in react-router.

How could one implement it such that they work together? Could you run an express server in front of webpack-dev-server in such a way to allow this?


You should set historyApiFallback of WebpackDevServer as true for this to work. Here's a small example (tweak to fit your purposes):

var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');

var config = require('./webpack.config');


var port = 4000;
var ip = '0.0.0.0';
new WebpackDevServer(webpack(config), {
    publicPath: config.output.publicPath,
    historyApiFallback: true,
}).listen(port, ip, function (err) {
    if(err) {
        return console.log(err);
    }

    console.log('Listening at ' + ip + ':' + port);
});

I set up a proxy to achieve this:

You have a regular express webserver that serves the index.html on any route, except if its an asset route. if it is an asset, the request gets proxied to the web-dev-server

your react hot entrypoints will still point directly at the webpack dev server, so hot reloading still works.

Let's assume you run webpack-dev-server on 8081 and your proxy at 8080. Your server.js file will look like this:

"use strict";
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./make-webpack-config')('dev');

var express = require('express');
var proxy = require('proxy-middleware');
var url = require('url');

## --------your proxy----------------------
var app = express();
## proxy the request for static assets
app.use('/assets', proxy(url.parse('http://localhost:8081/assets')));

app.get('/*', function(req, res) {
    res.sendFile(__dirname + '/index.html');
});


# -----your-webpack-dev-server------------------
var server = new WebpackDevServer(webpack(config), {
    contentBase: __dirname,
    hot: true,
    quiet: false,
    noInfo: false,
    publicPath: "/assets/",

    stats: { colors: true }
});

## run the two servers
server.listen(8081, "localhost", function() {});
app.listen(8080);

now make your entrypoints in the webpack config like so:

 entry: [
     './src/main.js',
     'webpack/hot/dev-server',
     'webpack-dev-server/client?http://localhost:8081'
 ]

note the direct call to 8081 for hotreload

also make sure you pass an absolute url to the output.publicPath option:

 output: {
     publicPath: "http://localhost:8081/assets/",
     // ...
 }

For anyone else that may still be looking for this answer. I put together a simple proxy bypass which achieves this without much hassle and the config goes into the webpack.config.js

I am sure there are much more elegant ways to test for local content using regex, but this works for my needs.

devServer: {
  proxy: { 
    '/**': {  //catch all requests
      target: '/index.html',  //default target
      secure: false,
      bypass: function(req, res, opt){
        //your custom code to check for any exceptions
        //console.log('bypass check', {req: req, res:res, opt: opt});
        if(req.path.indexOf('/img/') !== -1 || req.path.indexOf('/public/') !== -1){
          return '/'
        }

        if (req.headers.accept.indexOf('html') !== -1) {
          return '/index.html';
        }
      }
    }
  }
}