React-router v4 - cannot GET *url*
JavascriptReactjsRoutesLocalhostReact RouterJavascript Problem Overview
I started to use react-router v4. I have a simple <Router>
in my app.js with some navigation links (see code below). If I navigate to localhost/vocabulary
, router redirects me to the right page. However, when I press reload (F5) afterwards (localhost/vocabulary
), all content disappear and browser report Cannot GET /vocabulary
. How is that possible? Can somebody gives me any clue how to solve that (reload the page correctly)?
App.js:
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
import { Switch, Redirect } from 'react-router'
import Login from './pages/Login'
import Vocabulary from './pages/Vocabulary'
const appContainer = document.getElementById('app')
ReactDOM.render(
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/vocabulary">Vocabulary</Link></li>
</ul>
<Switch>
<Route exact path="/" component={Login} />
<Route exact path="/vocabulary" component={Vocabulary} />
</Switch>
</div>
</Router>,
appContainer)
Javascript Solutions
Solution 1 - Javascript
I'm assuming you're using Webpack. If so, adding a few things to your webpack config should solve the issue. Specifically, output.publicPath = '/'
and devServer.historyApiFallback = true
. Here's an example webpack config below which uses both of ^ and fixes the refresh issue for me. If you're curious "why", this will help.
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './app/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index_bundle.js',
publicPath: '/'
},
module: {
rules: [
{ test: /\.(js)$/, use: 'babel-loader' },
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ]}
]
},
devServer: {
historyApiFallback: true,
},
plugins: [
new HtmlWebpackPlugin({
template: 'app/index.html'
})
]
};
I wrote more about this here - Fixing the "cannot GET /URL" error on refresh with React Router (or how client side routers work)
Solution 2 - Javascript
Just to supplement Tyler's answer for anyone still struggling with this:
Adding the devServer.historyApiFallback: true
to my webpack config (without setting publicPath
) fixed the 404/Cannot-GET errors I was seeing on refresh/back/forward, but only for a single level of nested route. In other words, "/" and "/topics" started working fine but anything beyond that (e.g. "/topics/whatever") still threw a 404 on refresh/etc.
Just came across the accepted answer here: https://stackoverflow.com/questions/29718481/unexpected-token-error-in-react-router-component and it provided the last missing piece for me. Adding the leading /
to the bundle script src in my index.html
has solved the issue completely.
Solution 3 - Javascript
It worked for me just need just addding devServer { historyApiFallback: true }
is OK, not need use publicPath: '/'
usage like:
devServer: {
historyApiFallback: true
},
Solution 4 - Javascript
If your application is hosted on a static file server, you need to use a
import { HashRouter } from 'react-router-dom'
ReactDOM.render((
<HashRouter>
<App />
</HashRouter>
), holder)
Solution 5 - Javascript
I was having the same issue, what fixed it for me was editing my package.json
file, and under scripts: {
"build": "webpack -d && copy src\\index.html dist\\index.html /y && webpack-dev-server --content-base src --inline --port 3000"
at the end of my webpack build
code I added --history-api-fallback
this also seemed like the easiest solution to the Cannot GET /url
error
Note: the next time you build after adding --history-api-fallback
you will notice a line in the output that looks like this (with whatever your index file is):
> 404s will fallback to /index.html
Solution 6 - Javascript
If you are using Express (not Webpack), this worked for me..
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, 'path/to/your/index.html'), function(err) {
if (err) {
res.status(500).send(err)
}
})
})
Solution 7 - Javascript
I also had success with this by adding ... historyApiFallback: true
devServer: {
contentBase: path.join(__dirname, "public"),
watchContentBase: true,
publicPath: "/dist/",
historyApiFallback: true
}
Solution 8 - Javascript
If you use Webpack check your configuration in part of server configuration for the "contentBase" attribute. You can set by this example:
devServer: {
...
contentBase: path.join(__dirname, '../')
...
}
Solution 9 - Javascript
Adding to the accepted answer, if you are using the HtmlWebpackPlugin
and still getting errors double check the filename
property. If you are setting the filename
specifically for production then you will need to add a condition to set the filename
contingent on the WEBPACK_DEV_SERVER
variable. Please see the example below:
new HtmlWebpackPlugin({
template: './src/index.html',
filename: process.env.WEBPACK_DEV_SERVER ? 'index.html' : PROD_HTML_PATH,
})