Home > Software design >  React-Router V6 issue, only the root "/" route works, any other endpoint returns a 404
React-Router V6 issue, only the root "/" route works, any other endpoint returns a 404

Time:10-23

I'm trying to create a React app with multiple pages with React-Router V6. There seems to be an issue with the paths. Whenever I try running the code in my browser, any other path besides the root ("/"), returns a 404. Meaning that it doesn't exist. Here are snippets of the files I'm using:

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './components/App.js';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

App.js

import React from 'react';

// Components
import NavBar from './NavBar';
import Router from './Router';

const App = () => {
  return (
    <div id='app'>
      <Router />
    </div>
  )
}

export default App;

Router.js

import React from 'react';
import { Routes, Route, Link } from 'react-router-dom';

// Components
import Login from './Login';
import Checkout from './Checkout';
import History from './History';
import Home from './Home';

const Router = () => {
  return (
  <Routes>
    <Route path='/' element={<Home />} />
    <Route path='login' element={<Login />} />
    <Route path='/history' element={<History />} />
    <Route path='/checkout' element={<Checkout />} />
  </Routes>
  );
}

export default Router;

Home.js

import React from 'react';
import NavBar from './NavBar';

const Home = () => {
  return (
    <div id='home'>
      <NavBar />
      <h1>Homepage</h1>
    </div>
  );
}

export default Home;

Every other component's jsx code <Login>, <History> etc.. is identical to the <Home> one, other than the text in the <h1> tag

This is what happens when I open the root page /

This is what happens when I open any other endpoint /checkout /login

If this isn't enough information.... I can't make a minimal reproducible example as it takes too many files to create an up and running fullstack javascript application. But I can give you the link to the repo and some instructions on how to reproduce the issue. link: https://github.com/Lawsan92/pizzaOrder

  1. Fork and clone the repo
  2. open the file in any IDE
  3. Navigate to the server.js file and replace process.env.PORT with any port 3000 (or anything else)
  4. in the root directory run npm run server
  5. Open whichever browser you use and go to localhost:3000
  6. The test the other endpoints localhost:3000/login localhost:3000/checkout etc..

Here is my webpack config:

const path = require('path');
require('dotenv').config();

const clientCofig = {
  mode: 'development',
  entry: {
    path: path.join(__dirname, './client/src/index.js')
  },
  output: {
    path: path.join(__dirname, './client/dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /nodeModules/,
        loader: 'babel-loader'
      },
      {
        test:/\.css$/,
        loader: 'css-loader'
      }
    ]
  }
}

module.exports = clientCofig;

CodePudding user response:

Your webpack configuration needs to redirect all page requests to the root index.html file. You can do this by specifying a public path in the output and adding historyApiFallback: true, in the configuration. You'll also need to update the server.js file to handle nested page requests.

webpack.config.js

const path = require('path');
require('dotenv').config();

const clientCofig = {
  mode: 'development',
  entry: {
    path: path.join(__dirname, './client/src/index.js')
  },
  output: {
    path: path.join(__dirname, './client/dist'),
    filename: 'bundle.js',
    publicPath: '/' // <-- add public path here
  },
  devServer: {
    historyApiFallback: true // <-- add fallback here
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /nodeModules/,
        loader: 'babel-loader'
      },
      {
        test:/\.css$/,
        loader: 'css-loader'
      }
    ]
  }
}

module.exports = clientCofig;

server.js

const express = require('express');
const path = require('path');
const app = express();
require('dotenv').config();

app.get('/test', (req, res) => {
  res.status(200).send('hello world');
})

app.get('https://order-pizza-api.herokuapp.com/api/orders', (req, res) => {
  console.log('res.body:', res.body);
})

app.use(express.static(path.join(__dirname, '../client/dist')))

app.get('/*', function(req, res) { // <-- add
  res.sendFile(path.join(__dirname, '../client/dist/index.html'), function(err) {
    if (err) {
      res.status(500).send(err)
    }
  })
})

app.listen(process.env.PORT, () => {
  console.log(`listening to port: ${process.env.PORT}`)
})

app.use(express.json());

The other issue I found was in your NavBar component. It's rendering raw anchor tags <a> instead of the Link component from react-router-dom. Import the Link component and replace all anchor tags with the Link component and switch the href attribute to the to prop.

/src/components/NavBar.js

import React from 'react';
import { Link } from 'react-router-dom';

const NavBar = () => {
  return (
    <div className='nav'>
      <Link to='/' className='site-title'>
        Site Title
      </Link>
      <ul>
        <li className='active'>
          <Link to='/pricing'>Pricing</Link>
        </li>
        <li>
        <Link to='/history'>About</Link>
        </li>
      </ul>
    </div>
  );
};

export default NavBar;
  • Related