I try to setup nested routes for my react app like this
/ -> Home Page/about -> About Page/protected
basically wrap, your react app using <HashRouter> instead of <BrowserRouter> working fine without any webpack config modification, if you don't want to use HashRouter then you can free to use historyApiFallback: true in web pack dev server config on bottom of webpack.config file
like so
const config = {
........
devServer: {
compress: true,
port: 3000,
https: false,
historyApiFallback:true
}
}
I finally figured out the reason that webpack-dev-server couldn't serve nested routes.
As a single page application, when you visit /somepath of your react app, it actually fallback to the / and pass the pathname to react router. React router will navigate you to /somepath by the using browser's history API.
webpack-dev-server, for some unknown reason, doesn't enable this "fallback to history API" behaviour by default.
So, we need to add historyApiFallback: true, to the devServer of webpack config.
Now, all top level routes, like /somepath should work, but for nested routes, like /somepath/morepath, it's not enough.
With default webpack-dev-server setting, the compiled html template will point to the bundled js like <script type="text/javascript" src="main.js"></script>. Pay attention to the src="main.js" which assumes the main.js is under the same path as the index.html. The assumption is true for top level path /somepath but for nested routes, /somepath/morepath, this assumption will lead html file to access main.js like /somepath/main.js.
So, we end up with looking for a way to specify a certain place for html file when it's going to access the bundled js. And, it's the job of publicPath. Adding publicPath: '/', to the output block of webpack config. It will tell html to always access main.js from / folder and the compiled html will be <script type="text/javascript" src="/main.js"></script>. That's exactly what we're looking for.
Try adding:
<base href="/" />
to the <head> tag of your index.html. This way it'll always look for /main.js bundle, even for nested routes.
To summarized the answer by @Bing Lu, in your webpack.config.js file:
module.exports = () => ({
mode: 'development',
entry: ...,
...,
output: {
...
publicPath: '/' // <- this is the important line along with historyApiFallback = true in the dev server config
},
...,
devServer: {
contentBase: path.join(__dirname, 'dist'),
historyApiFallback: true,
compress: true,
...
},
})
I was having the same problem described in the question (webpack-dev-server not serving nested routes, top level ones working fine). Sadly, neither historyApiFallback: true nor publicPath: '/' were working. Actually, the problem was inside index.html, more precisely inside <script src="bundle.js"></script>. Changing to
<script src="/bundle.js"></script> <!-- do include slash before the file name -->
was enough to end the pain.