Using react-router v6 I need a navbar to permanently be there, but can't display both <App/> and <Home/> on my index page, how to fix?

Viewed 3955

I am rebuilding my personal site with React and have decided on the latest version of react-router-dom, I am building my App component to contain the navbar and then underneath the content I want to display, such as Home or Projects. This works fine on my /projects route but I cannot seem to work out how to render both App and Home on the index route. Is there a way? Here is what I have so far:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter, Link, Route, Routes } from 'react-router-dom';
// N.B. Routes replaces Switch as of V6

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Routes>
        // Using <App/> here keeps the navbar but doesn't display the homepage content
        <Route path="/" element={<App/>}>
        // But using <Home/> loses the navbar
        // Should I have my Navbar component in here somewhere or within the App?
          <Route path="projects" element={<Projects/>}/>
        </Route>
      </Routes>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

function App() {
  // Help?
}

function Navbar() {
  return (
    <header> // Very crude example as I'm self-answering, in production I'll rename the component to Header
      <nav>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/projects">Projects</Link>
          </li>
        </ul>
      </nav>
    </header>
}

function Home() {
  return <h1>Home</h1>
}

function Projects() {
  return <h1>Projects</h1>
}
2 Answers

The app can just unconditionally render the Navbar, and move the Routes component and routing to App. The index file renders App into the BrowserRouter.

App

function App() {
  return (
    <>
      <Navbar />
      <Routes>
        <Route path="/" element={<Home/>}>
        <Route path="projects" element={<Projects/>}/>
      </Routes>
    </>
  );
}

index.js

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

First of all, here is what your App component will look like:

function App() {
  return (
    <div className="App">
      <Navbar/>
      <main> // You don't *have* to wrap the Outlet in a main tag, but I am doing, to save having to remember to do it in each component
        <Outlet/>
      </main>
    </div>
  )
}

So that now your App always displays the Navbar and the outlet will display whichever page the route tells it to.

Okay that's great, but it still means rendering App when I want to be rendering Home and still keeping that navbar, does this really fix that?

Well, not on its own. First add that Outlet component to your import from react-router-dom, otherwise we're getting nowhere!

Now adjust your routing like so:

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

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App/>}>
          <Route index element={<Home/>}/>
          <Route path="projects" element={<Projects/>}/>
        </Route>
      </Routes>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

See how that Route tag with the Home component has the index tag on it? That's it, that's the solution! Now your App renders as it should, navbar included, and its Outlet renders the Home element on the index route.

Related