React-Router-Dom-v6 Nesting Navigations
In this blog, I will be giving a walkthrough on how to set up navigation with nested links in react-router-dom v6. Let's get started!
Install react-router-dom-v6:
npm install react-router-dom@6
Create a routes.js file in the src folder. In this file, you will add as many routes as you want to have in your project. To keep things simple we are going to have a parent route with a couple of nested routes as children.
//In routes.js
import App from './App'
import Movies from './pages/Movies'
import MovieForm from './pages/MovieForm'
const routes =[
{
path:'/',
element: <App />,
children: [
{
path: '/',
element: <Movies />
},
{
path: '/add-movie',
element: <MovieForm />
}
]
}
]
export default routes
Now we have to use a few tools from react-router-dom to connect our routes to the rest of our react application.
In index.js add the following code:
//In index.js
//add these imports
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import routes from './routes'
//add this variable
const router=createBrowserRouter(routes)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
{/* add this component and pass in the router*/}
<RouterProvider router={router} />
</React.StrictMode>
);
In the code above we are creating the routes we want with the createBrowserRouter
function and the RouterProvider
provides the routes to our application so we can do some client-side routing.
Now that we have created our routes and provided them to the rest of our application, we can create some links to point to our routes using the NavLink component from react-router-dom.
Let's create a NavBar
component where we can store our links.
//In Navbar.js
import React from 'react'
import { NavLink } from 'react-router-dom'
const NavBar = () => {
return (
<nav>
<NavLink to='/'>Movies</NavLink>
<NavLink to='/add-movie'>Add A Movie</NavLink>
</nav>
)
}
export default NavBar
Now import the Navbar in App.js.
//In App.js
import NavBar from './NavBar';
function App() {
return (
<div className="App">
<header>
<NavBar />
</header>
</div>
);
}
export default App;
If you run npm start, you will see your 'Movies' and 'Add A Movie' links. However, if you try to navigate to them, you'll get a nasty error-looking page. This is because the 'Movies' and 'Add A Movie' components are nested. So we need to use the Outlet component. This component will point to the nested routes and render the components for those nested routes. The Outlet component needs to be called in the parent component. In this case, it will be in the App component.
In the App component import Outlet
:
//In App.js
import './App.css';
import NavBar from './NavBar';
//import Outlet component
import { Outlet } from 'react-router-dom';
function App() {
return (
<div className="App">
<header>
<NavBar />
</header>
{/*Add this line*/}
<Outlet />
</div>
);
}
export default App;
You can now navigate to the Movies or Add A Movie pages.
There is one more cool feature you can take advantage of when using the Outlet component. The Outlet component can receive a context
prop that takes data as its value. You can avoid 'prop drilling' by using the context
prop. Say you want to fetch data from the parent component and you want the child components to have access to that data. You can make a fetch request in the App component (or any parent component of nested routes), pass the data to the Outlet component, and use useOutletContext
in the child components to render the data however you like.
//In App.js
//...some code
import {useState, useEffect} from 'react'
function App() {
//add a state that will store your data
const [toys, setToys] = useState([])
//Make a fetch request
useEffect(() =>{
fetch("http://localhost:4000/toys")
.then(r => r.json())
.then(data => setToys(data))
.catch(error => console.error(error));
}, []);
return (
<div className="App">
<header>
<NavBar />
</header>
{/*add the context prop and pass in the data*/}
<Outlet context={toys} />
</div>
);
}
In the code above we are passing data to the Outlet component. Now we are going to invoke the useOutletContext function in the child components.
//In Movies.js
import {useOutletContext} from 'react-router-dom'
const Home = () => {
const toys = useOutletContext()
console.log(toys)
//you can now use the data however you like in this component.
//this setup will also work for the MovieForm component
return (
<div>
Movies Page
</div>
)
}
By invoking useOutletContext
, it will return the data we passed into the Outlet component and assign it to a toys variable where we can then render the data in whatever format we like.
Remember that anytime you have nested routes, to render the components for those routes you must use the Outlet component in the parent component. You can then use the useOutletContext
function to use that data. One more thing to add, if you notice in the routes.js file, the parent's path and the first child's path are the same. This is okay to do but only one child can have the same path as the parent, the rest must have a different path.