195

React router allows react apps to handle /arbitrary/route. In order this to work, I need my server to send the React app on any matched route.

But webpack dev server doesn't handle arbitrary end points.

There is a solution here using additional express server. How to allow for webpack-dev-server to allow entry points from react-router

But I don't want to fire up another express server to allow route matching. I just want to tell webpack dev server to match any url and send me my react app. please.

Community
  • 1
  • 1
eguneys
  • 6,028
  • 7
  • 31
  • 63
  • have you seen [React Router Mega Demo](https://github.com/rackt/react-router-mega-demo)? – rojobuffalo Aug 11 '15 at 15:38
  • 1
    Possible duplicate of [How to allow for webpack-dev-server to allow entry points from react-router](https://stackoverflow.com/questions/26203725/how-to-allow-for-webpack-dev-server-to-allow-entry-points-from-react-router) – Mišo Jan 22 '19 at 14:48

11 Answers11

208

I found the easiest solution to include a small config:

  devServer: {
    port: 3000,
    historyApiFallback: {
      index: 'index.html'
    }
  }

I found this by visiting: PUSHSTATE WITH WEBPACK-DEV-SERVER.

Edgar
  • 6,022
  • 8
  • 33
  • 66
cmfolio
  • 3,413
  • 1
  • 16
  • 13
112

historyApiFallback option on official documentation for webpack-dev-server explains clearly how you can achieve either by using

historyApiFallback: true

which simply falls back to index.html when the route is not found

or

// output.publicPath: '/foo-app/'
historyApiFallback: {
  index: '/foo-app/'
}
skellertor
  • 916
  • 1
  • 9
  • 26
G G
  • 1,614
  • 1
  • 12
  • 12
  • 1
    Updated link: https://webpack.js.org/configuration/dev-server/#devserver – Jakob Jingleheimer Apr 21 '18 at 20:25
  • But actually webpack-dev-server is in maintenance now. It's successor is https://github.com/webpack-contrib/webpack-serve#add-function-parameters, which supports `historyApiFallback` – Jakob Jingleheimer Apr 21 '18 at 20:44
  • 3
    For anybody reading this in 2019, according to https://github.com/webpack-contrib/webpack-serve#webpack-serve `webpack-dev-server` is the successor to `webpack-serve`, not the other way around as mentioned in https://stackoverflow.com/questions/31945763/how-to-tell-webpack-dev-server-to-serve-index-html-for-any-route?answertab=oldest#comment86936567_39985334. – ur5us Jan 11 '19 at 04:00
  • ur5us' comment is actually false. webpack-serve was the planned successor to webpack-dev-server. I'm the author of webpack-serve and former maintainer of webpack-dev-server. when I took some time off, bitter org members deprecated webpack-serve, and I have since released it under my fork. – shellscape Mar 18 '19 at 19:15
44

Adding public path to config helps webpack to understand real root (/) even when you are on subroutes, eg. /article/uuid

So modify your webpack config and add following:

output: {
    publicPath: "/"
}

devServer: {
    historyApiFallback: true
}

Without publicPath resources might not be loaded properly, only index.html.

Tested on Webpack 4.6

Larger part of config (just to have better picture):

entry: "./main.js",
output: {
  publicPath: "/",
  path: path.join(__dirname, "public"),
  filename: "bundle-[hash].js"
},
devServer: {
  host: "domain.local",
  https: true,
  port: 123,
  hot: true,
  contentBase: "./public",
  inline: true,
  disableHostCheck: true,
  historyApiFallback: true
}
Jurosh
  • 6,984
  • 7
  • 40
  • 51
  • 1
    Wow this worked for me as well! The `historyApiFallback` trick only worked for the last part of the URL for some reason. `/test` would work but `/test/test` would give 404. – Alex. P. Dec 17 '19 at 21:44
  • In addition to `historyApiFallback: {index: '/'} ` or `historyApiFallback: true` (both worked for me), setting the `publicPath` was also essential in my case (Router 5.2). – Marcus Junius Brutus Jun 05 '20 at 11:59
  • 1
    The only answer solving whole problem. Thanks man! – Tomáš Vavřinka Dec 12 '20 at 16:21
18

Works for me like this

devServer: {
    contentBase: "./src",
    hot: true,
    port: 3000,
    historyApiFallback: true

},

Working on riot app

user2088033
  • 181
  • 1
  • 2
14

My situation was a little different, since I am using the angular CLI with webpack and the 'eject' option after running the ng eject command. I modified the ejected npm script for 'npm start' in the package.json to pass in the --history-api-fallback flag

"start": "webpack-dev-server --port=4200 --history-api-fallback"

"scripts": {
"ng": "ng",
"start": "webpack-dev-server --port=4200 --history-api-fallback",
"build": "webpack",
"test": "karma start ./karma.conf.js",
"lint": "ng lint",
"e2e": "protractor ./protractor.conf.js",
"prepree2e": "npm start",
"pree2e": "webdriver-manager update --standalone false --gecko false --quiet",
"startold": "webpack-dev-server --inline --progress --port 8080",
"testold": "karma start",
"buildold": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"},
Brandon Culley
  • 5,219
  • 1
  • 28
  • 28
8

I agree with the majority of existing answers.

One key thing I wanted to mention is if you hit issues when manually reloading pages on deeper paths where it keeps the all but the last section of the path and tacks on the name of your js bundle file you probably need an extra setting (specifically the publicPath setting).

For example, if I have a path /foo/bar and my bundler file is called bundle.js. When I try to manually refresh the page I get a 404 saying /foo/bundle.js cannot be found. Interestingly if you try reloading from the path /foo you see no issues (this is because the fallback handles it).

Try using the below in conjunction with your existing webpack config to fix the issue. output.publicPath is the key piece!

output: {
    filename: 'bundle.js',
    publicPath: '/',
    path: path.resolve(__dirname, 'public')
},
...
devServer: {
    historyApiFallback: true
}
6

If you choose to use webpack-dev-server, you should not use it to serve your entire React app. You should use it to serve your bundle.js file as well as the static dependencies. In this case, you would have to start 2 servers, one for the Node.js entry points, that are actually going to process routes and serve the HTML, and another one for the bundle and static resources.

If you really want a single server, you have to stop using the webpack-dev-server and start using the webpack-dev-middleware within your app-server. It will process bundles "on the fly" (I think it supports caching and hot module replacements) and make sure your calls to bundle.js are always up to date.

Andre Pena
  • 56,650
  • 48
  • 196
  • 243
  • 2
    I am using webpack-dev-server only for development hot reloading source maps etc. Otherwise I have a static website where I can host the files from anywhere. – eguneys Aug 11 '15 at 16:32
6

For me I had dots "." in my path e.g. /orgs.csv so I had to put this in my webpack confg.

devServer: {
  historyApiFallback: {
    disableDotRule: true,
  },
},
GentryRiggen
  • 788
  • 10
  • 10
5

You can enable historyApiFallback to serve the index.html instead of an 404 error when no other resource has been found at this location.

let devServer = new WebpackDevServer(compiler, {
    historyApiFallback: true,
});

If you want to serve different files for different URIs, you can add basic rewriting rules to this option. The index.html will still be served for other paths.

let devServer = new WebpackDevServer(compiler, {
    historyApiFallback: {
        rewrites: [
            { from: /^\/page1/, to: '/page1.html' },
            { from: /^\/page2/, to: '/page2.html' },
            { from: /^\/page3/, to: '/page3.html' },
        ]
    },
});
JojOatXGME
  • 3,023
  • 2
  • 25
  • 41
2

I know this question is for webpack-dev-server, but for anyone who uses webpack-serve 2.0. with webpack 4.16.5; webpack-serve allows add-ons.You'll need to create serve.config.js:

const serve = require('webpack-serve');
const argv = {};
const config = require('./webpack.config.js');

const history = require('connect-history-api-fallback');
const convert = require('koa-connect');

serve(argv, { config }).then((result) => {
  server.on('listening', ({ server, options }) => {
      options.add: (app, middleware, options) => {

          // HistoryApiFallback
          const historyOptions = {
              // ... configure options
          };

          app.use(convert(history(historyOptions)));
      }
  });
});

Reference

You will need to change the dev script from webpack-serve to node serve.config.js.

yotke
  • 1,170
  • 2
  • 12
  • 26
0

To those who are still struggling to get this working with webpack > 5 like me here is the working configuration

//webpack.config.js

const path = require('path');

module.exports = {
    entry: './src/main.js',
    output: {
        path: path.join(__dirname, 'output'),
        filename: 'bundle.js',
        
    },
    devServer: {
        port: 3000,        
        historyApiFallback:true,
        static: './'
    }
}

index.html file is at the root level.

Dipak Telangre
  • 1,792
  • 4
  • 19
  • 46