Welcome to the React Router tutorial! This guide is designed to help you understand how routing works in React applications using the popular react-router-dom
library. We'll cover everything from setting up basic routes to handling nested routes.
- Introduction
- Setting Up Routing
- Navigation
- Passing Data with Route Parameters
- Getting Data about the Current Route
- Nested Routes
To get started, ensure you have react-router-dom
installed in your project:
npm install react-router-dom
React Router is a standard library for routing in React. It enables the implementation of dynamic routing in a web application. A dynamic route is one where the navigation between components is handled internally by the JavaScript without the need to reload the page.
First, let's set up the basic routing in a React application. Below is how you would set up a BrowserRouter
and define some routes using react-router-dom
:
// Import necessary components from react-router-dom
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import HomePage from './HomePage';
import UserListPage from './UserListPage';
// Create a router.tsx file
const router = createBrowserRouter([
{ path: '/', element: <HomePage /> },
{ path: '/users', element: <UserListPage /> },
]);
// Use RouterProvider to integrate routing into your app
function App() {
return (
<RouterProvider router={router} />
);
}
export default App;
For navigation between pages, React Router provides Link
and useNavigate
:
import { Link } from 'react-router-dom';
function Navbar() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/users">Users</Link>
</nav>
);
}
import { useNavigate } from 'react-router-dom';
function HomeButton() {
let navigate = useNavigate();
return (
<button onClick={() => navigate('/')}>
Go Home
</button>
);
}
To pass and retrieve parameters through routes, use useParams
:
import { useParams } from 'react-router-dom';
function UserDetail() {
let { userId } = useParams();
return <h2>User ID: {userId}</h2>;
}
And the route would be defined as:
{ path: '/users/:userId', element: <UserDetail /> }
Using useLocation
and useSearchParams
to manage query parameters and state:
import { useLocation } from 'react-router-dom';
function CurrentLocation() {
let location = useLocation();
console.log(location);
return <p>Current path: {location.pathname}</p>;
}
import { useSearchParams } from 'react-router-dom';
function SearchParamsComponent() {
let [searchParams, setSearchParams] = useSearchParams();
return (
<div>
<p>Query: {searchParams.get('query')}</p>
<button onClick={() => setSearchParams({ query: 'newQuery' })}>
Update Search
</button>
</div>
);
}
Nested routes allow components to have their own sub-routes. Here is how you could implement nested routes:
import { createBrowserRouter, RouterProvider, Outlet } from 'react-router-dom';
import UserDetail from './UserDetail';
const router = createBrowserRouter([
{
path: 'users',
element: <UserListPage />,
children: [
{ path: ':userId', element: <UserDetail /> },
],
},
]);
function UserListPage() {
return (
<div>
<h1>Users</h1>
<Outlet /> {/* Where nested routes render */}
</div>
);
}
In the example above, when you navigate to /users/1
, the UserListPage
component renders, and within it, UserDetail
renders in place of <Outlet />
displaying the details for user 1. This setup facilitates the creation of complex user interfaces where different segments of the UI depend on the hierarchy of the routes.
Here's an explanation of each setup and how they differ:
In the configuration you've mentioned:
const router = createBrowserRouter([
{
path: '/users',
element: <UserListPage />,
},
{ path: '/users/:userId', element: <UserDetail /> }
]);
This setup is a flat route configuration. Here’s what happens:
/users
: This route will render theUserListPage
component./users/:userId
: This route will render theUserDetail
component, but it does not renderUserListPage
. When you navigate to/users/:userId
, only theUserDetail
component is displayed, and theUserListPage
is unmounted.
This arrangement is useful when you want each route to independently manage its rendering and the parent-child relationship doesn't require maintaining any shared layout or state through an <Outlet>
.
In contrast, a nested route setup looks like this:
const router = createBrowserRouter([
{
path: '/users',
element: <UserListPage />,
children: [
{ path: ':userId', element: <UserDetail /> }
]
}
]);
In this scenario:
/users
: This route renders theUserListPage
component./users/:userId
: This route still renders theUserListPage
, but it also rendersUserDetail
in place of an<Outlet>
withinUserListPage
.
The nested route configuration is particularly advantageous when:
- Shared Layout: You want to maintain a common layout or elements, such as headers, footers, navigation bars, or sidebars, that should not re-render when navigating between parent and child routes.
- Contextual Information: You want to maintain context or state from the parent route that might be needed by the child routes, enhancing performance by avoiding re-fetching data or recalculating derived data.
- Hierarchical Relationships: You are representing a hierarchical relationship in the UI, such as viewing a list of items (
UserListPage
) and then a detailed view of one item (UserDetail
) where the list is still visible or accessible.
The choice between flat and nested routes should be guided by the specific needs of your application's structure and behavior:
- Flat Routes are simpler and suitable when each route is a completely independent view.
- Nested Routes offer more complex arrangements that support a structured hierarchy, making them ideal for applications with layers of related content.
In summary, the nested routes allow components to remain mounted and visible, or to preserve their state when navigating to child routes, whereas flat routes do not inherently offer this capability.