React Router Basics
React Router Basics
React Router is the standard routing library for React applications. It enables navigation between different components, changing the browser URL while keeping the UI in sync.
Installation
To use React Router in your project, install it via npm:
npm install react-router-dom
Basic Setup
BrowserRouter
import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// Components
function Home() {
return <h1>Welcome to Home Page</h1>;
}
function About() {
return <h1>About Us</h1>;
}
function Contact() {
return <h1>Contact Us</h1>;
}
// App with routing
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</BrowserRouter>
);
}
Navigation with Link
Basic Navigation
import { Link } from 'react-router-dom';
function Navigation() {
return (
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/contact">Contact</Link></li>
</ul>
</nav>
);
}
// App with navigation
function App() {
return (
<BrowserRouter>
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</BrowserRouter>
);
}
NavLink for Active Styling
import { NavLink } from 'react-router-dom';
function Navigation() {
return (
<nav>
<NavLink
to="/"
className={({ isActive }) => isActive ? "active" : ""}
>
Home
</NavLink>
<NavLink
to="/about"
className={({ isActive }) => isActive ? "active" : ""}
>
About
</NavLink>
<NavLink
to="/contact"
style={({ isActive }) => ({
color: isActive ? 'red' : 'black',
fontWeight: isActive ? 'bold' : 'normal'
})}
>
Contact
</NavLink>
</nav>
);
}
Route Parameters
Dynamic Routes
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users" element={<Users />} />
<Route path="/users/:userId" element={<UserDetail />} />
<Route path="/products/:category/:id" element={<Product />} />
</Routes>
</BrowserRouter>
);
}
// Accessing route parameters
import { useParams } from 'react-router-dom';
function UserDetail() {
const { userId } = useParams();
return (
<div>
<h1>User Details</h1>
<p>User ID: {userId}</p>
</div>
);
}
function Product() {
const { category, id } = useParams();
return (
<div>
<h1>Product Details</h1>
<p>Category: {category}</p>
<p>Product ID: {id}</p>
</div>
);
}
Nested Routes
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="dashboard/*" element={<Dashboard />} />
</Route>
</Routes>
</BrowserRouter>
);
}
// Layout component with Outlet
import { Outlet, Link } from 'react-router-dom';
function Layout() {
return (
<div>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/dashboard">Dashboard</Link>
</nav>
<main>
<Outlet /> {/* Child routes render here */}
</main>
<footer>© 2024 My App</footer>
</div>
);
}
// Dashboard with nested routes
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<nav>
<Link to="profile">Profile</Link>
<Link to="settings">Settings</Link>
</nav>
<Routes>
<Route index element={<DashboardHome />} />
<Route path="profile" element={<Profile />} />
<Route path="settings" element={<Settings />} />
</Routes>
</div>
);
}
404 Not Found Route
function NotFound() {
return (
<div>
<h1>404 - Page Not Found</h1>
<p>The page you're looking for doesn't exist.</p>
<Link to="/">Go to Home</Link>
</div>
);
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
Programmatic Navigation
Using useNavigate Hook
import { useNavigate } from 'react-router-dom';
function LoginForm() {
const navigate = useNavigate();
const [username, setUsername] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
// Perform login logic
if (username === 'admin') {
// Navigate to dashboard
navigate('/dashboard');
// Navigate with replace (no back button)
// navigate('/dashboard', { replace: true });
// Navigate with state
// navigate('/dashboard', { state: { from: 'login' } });
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Username"
/>
<button type="submit">Login</button>
<button type="button" onClick={() => navigate(-1)}>
Go Back
</button>
</form>
);
}
Query Parameters
import { useSearchParams } from 'react-router-dom';
function SearchPage() {
const [searchParams, setSearchParams] = useSearchParams();
const query = searchParams.get('q') || '';
const category = searchParams.get('category') || 'all';
const page = parseInt(searchParams.get('page') || '1');
const updateSearch = (newQuery) => {
setSearchParams({
q: newQuery,
category,
page: 1 // Reset to page 1 on new search
});
};
const changePage = (newPage) => {
setSearchParams({
q: query,
category,
page: newPage
});
};
return (
<div>
<h1>Search Results</h1>
<input
value={query}
onChange={(e) => updateSearch(e.target.value)}
placeholder="Search..."
/>
<select
value={category}
onChange={(e) => setSearchParams({ q: query, category: e.target.value, page: 1 })}
>
<option value="all">All Categories</option>
<option value="books">Books</option>
<option value="movies">Movies</option>
</select>
<p>Searching for: {query} in {category}</p>
<p>Page: {page}</p>
<button onClick={() => changePage(page - 1)} disabled={page === 1}>
Previous
</button>
<button onClick={() => changePage(page + 1)}>
Next
</button>
</div>
);
}
Route Configuration
Centralized Route Config
const routes = [
{
path: '/',
element: <Layout />,
children: [
{ index: true, element: <Home /> },
{ path: 'about', element: <About /> },
{
path: 'products',
element: <Products />,
children: [
{ index: true, element: <ProductList /> },
{ path: ':id', element: <ProductDetail /> }
]
},
{ path: 'contact', element: <Contact /> },
{ path: '*', element: <NotFound /> }
]
}
];
function App() {
return (
<BrowserRouter>
<Routes>
{routes.map((route, index) => (
<Route key={index} path={route.path} element={route.element}>
{route.children?.map((child, childIndex) => (
<Route
key={childIndex}
index={child.index}
path={child.path}
element={child.element}
/>
))}
</Route>
))}
</Routes>
</BrowserRouter>
);
}
Lazy Loading Routes
import { lazy, Suspense } from 'react';
// Lazy load components
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard/*" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
Scroll Restoration
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
function App() {
return (
<BrowserRouter>
<ScrollToTop />
<Routes>
{/* Your routes */}
</Routes>
</BrowserRouter>
);
}
Route Guards Example
function PrivateRoute({ children }) {
const isAuthenticated = checkAuth(); // Your auth check
const location = useLocation();
if (!isAuthenticated) {
// Redirect to login page but save the attempted location
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/dashboard"
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
/>
</Routes>
</BrowserRouter>
);
}
Custom Router Hook
function useRouter() {
const navigate = useNavigate();
const location = useLocation();
const params = useParams();
const [searchParams, setSearchParams] = useSearchParams();
return {
navigate,
location,
params,
searchParams,
setSearchParams,
// Helper methods
goBack: () => navigate(-1),
goForward: () => navigate(1),
push: (path, state) => navigate(path, { state }),
replace: (path, state) => navigate(path, { replace: true, state })
};
}
// Usage
function MyComponent() {
const router = useRouter();
const handleClick = () => {
router.push('/dashboard', { from: 'home' });
};
return (
<div>
<p>Current path: {router.location.pathname}</p>
<button onClick={router.goBack}>Go Back</button>
<button onClick={handleClick}>Go to Dashboard</button>
</div>
);
}
Best Practices
- Use BrowserRouter for web apps (not HashRouter)
- Organize routes in a centralized configuration
- Implement lazy loading for better performance
- Handle 404s with a catch-all route
- Use NavLink for navigation menus
- Protect private routes with route guards
- Handle loading states during navigation
- Restore scroll position on route changes
React Router is essential for building single-page applications with multiple views. Master these concepts to create seamless navigation experiences!