skip to Main Content

I’ve been going through some other threads but have not found an answer to help me out. I’m building a very simple E-commerce website. The router has been working fine until I get to a route like "localhost:3000/shop/1" or "localhost:3000/shop/2", where it continues to display the <Shop /> element that I should only see on the "localhost:3000/shop" route instead of the <Item /> element. While I’ve found a workaround, I’m confused why only the workaround is valid and would love to see if there’s a way to make it work for the first way that I have it formatted.

Here’s what doesn’t work:

App.tsx

import { Route, Routes } from 'react-router-dom';
import Home from './pages/Home'
import Shop from './pages/Shop';
import Item from './pages/Item';
import CheckOut from './pages/Checkout';
import NotFound from './pages/NotFound';
import NavBar from './assets/navbar';

function App() {
  return (
    <>
      <Routes>
        <Route element={<NavBar />}>
          <Route path='/' element={<Home />} />
          <Route path='shop/' element={<Shop />}>
            <Route path=':id' element={<Item />} />
          </Route>
          <Route path='*' element={<NotFound />} />
        </Route>
        <Route path='checkout' element={<CheckOut />} />
        <Route path='*' element={<NotFound />} />
      </Routes>
    </>
  )
}

export default App;

I’ve tried changing <Route path='shop/' element={<Shop />}> to <Route path='shop' element={<Shop />}> and still there has been no change in the functionality.

Here’s what does work:

App.tsx

import { Route, Routes } from 'react-router-dom';
import Home from './pages/Home'
import Shop from './pages/Shop';
import Item from './pages/Item';
import CheckOut from './pages/Checkout';
import NotFound from './pages/NotFound';
import NavBar from './assets/navbar';

function App() {
  return (
    <>
      <Routes>
        <Route element={<NavBar />}>
          <Route path='/' element={<Home />} />
          <Route path='shop' element={<Shop />} />
          <Route path='shop/:id' element={<Item />} />
          <Route path='*' element={<NotFound />} />
        </Route>
        <Route path='checkout' element={<CheckOut />} />
        <Route path='*' element={<NotFound />} />
      </Routes>
    </>
  )
}

export default App;

Is there a reason why one way does work and the other doesn’t?

2

Answers


  1. import {Route, Routes} from 'react-router-dom';
    
    import Home from './pages/Home'
    import Shop from './pages/Shop';
    import Item from './pages/Item';
    import CheckOut from './pages/Checkout';
    import NotFound from './pages/NotFound';
    import NavBar from './assets/navbar';
    
    function App() {
        return (
           <>
                <Routes>
                    <Route element={<NavBar />}>
                        <Route path='/' element={<Home />} />
                        <Route path='/shop' element={<Shop />} />
                        <Route path='/shop/:id' element={<Item />} />
                        <Route path='*' element={<NotFound />} />
                    </Route>
                    <Route path='/checkout' element={<CheckOut />} />
                    <Route path='*' element={<NotFound />} />
                </Routes>
            </>
        )
    }
    
    export default App;
    Login or Signup to reply.
  2. If when the URL path is "/shop/1" or "/shop/2" and only the Shop component is still rendered then I suspect the Shop component is missing rendering an Outlet component for the nested routes it renders.

    Add the missing Outlet to Shop so nested routes like "/shop/:id" can render their element content into it. This is similar what NavBar is doing to render the "/" and "/shop" nested routes.

    Example:

    import { Outlet } from 'react-router-dom';
    
    const Shop = () => {
      ... shop business logic ...
    
      return (
        ... Shop UI ...
        <Outlet /> // <-- nested route content rendered here
        ...
      );
    };
    
    function App() {
      return (
        <Routes>
          <Route element={<NavBar />}>
            <Route path='/' element={<Home />} />
            <Route path='shop' element={<Shop />}>
              <Route path=':id' element={<Item />} /> // <-- rendered into Shop Outlet
            </Route>
          </Route>
          <Route path='checkout' element={<CheckOut />} />
          <Route path='*' element={<NotFound />} />
        </Routes>
      );
    }
    

    For more information and details see:

    If it is the case that you want Shop and Item to each render on their own discrete routes then un-nest the Item route and render them as sibling routes as you do in your second code example.

    Example:

    function App() {
      return (
        <Routes>
          <Route element={<NavBar />}>
            <Route path='/' element={<Home />} />
            <Route path='shop'>
              <Route index element={<Shop />} />
              <Route path=':id' element={<Item />} />
            </Route>
          </Route>
          <Route path='checkout' element={<CheckOut />} />
          <Route path='*' element={<NotFound />} />
        </Routes>
      );
    }
    

    For more information see:

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search