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.