118

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)
1

11 Answers 11

318

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)

Sign up to request clarification or add additional context in comments.

14 Comments

worked by added devServer: { historyApiFallback: true, },
And don't use HtmlWebpackPlugin with index.html as a template as the bundle is not injected on refreshing on a route. Insert the bundle.js script directly as a script tag in the index.html.
It worked for me just addding devServer { historyApiFallback: true } I already had publicPath: '/' configured
+1 from me and do not forget to restart server (if you have hotload enabled) every time you change webpack.config.js.
It works on devServer. but after build Its not works :(
|
17

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: 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.

Comments

12

It worked for me just need just addding devServer { historyApiFallback: true } is OK, not need use publicPath: '/'

usage like:

  devServer: {
    historyApiFallback: true
  },

Comments

9

If your application is hosted on a static file server, you need to use a instead of a .

import { HashRouter } from 'react-router-dom'

ReactDOM.render((
  <HashRouter>
    <App />
  </HashRouter>
), holder)

https://github.com/ReactTraining/react-router/blob/v4.1.1/FAQ.md#why-doesnt-my-application-render-after-refreshing

1 Comment

The article you've referenced was really helpful because I was using express.js. This code from the article gave me the hint and solution of accepting different URL aside from the "/" since it has an asterisk as an argument of get() function. app.get('*', (req, res) => { res.sendFile(path.resolve(__dirname, 'index.html')) })
7

Works with React-router v6 and Webpack 5
(https://webpack.js.org/configuration/dev-server/#devservermagichtml):

devServer: {
   magicHtml: true,
   historyApiFallback: true,
},
plugins: [
   new HtmlWebpackPlugin({
      template: path.resolve(__dirname, 'src/index.html'),
      publicPath: '/',
   })
]

1 Comment

Up to dated solution 👍
3

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

Comments

3

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)
    }
  })
})

1 Comment

Where do you have to write this code ? In which file of the backend ? I have app.js , index.js and then inside controllers different routers. I am using express.js. Also about the path of index.html. Which path are we talking about ? I have the frontend build folder inside my root directory of backend.
2

I also had success with this by adding ... historyApiFallback: true

devServer: {
    contentBase: path.join(__dirname, "public"),
    watchContentBase: true,
    publicPath: "/dist/",
    historyApiFallback: true
}

1 Comment

Where do i add this?
1

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, '../')
    ...
}

Comments

0

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,
})

2 Comments

what's PROD_HTML_PATH ?
It is an arbitrary variable name for the path you would want to use for production. The variable could be named anything @7urkm3n
0
If you are using webpack. You need to add 

        devServer: {
            historyApiFallback: true,
          }
        
        Example :: 
        
        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'
            })
          ]
        };

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.