Reusable React Code Modules, Part 3 - Routing and Navigation
Seamless Navigation: Building Reusable Routing Modules for Scalable Applications
Routing and navigation are critical components of any front-end web application. In React, implementing a robust and reusable routing system ensures maintainability and scalability. This post will cover the most common libraries and tools for routing and navigation in React, comparing their features and providing practical examples and code snippets
Tools
React Router
Next.js
Reach Router
Wouter
Reach Router
https://reach.tech/router
Wouter
https://github.com/molefrog/wouter
Common Libraries and Tools
1. React Router
React Router is the most widely used library for routing in React applications
It provides a tools for managing navigation and routing within React applications
Key Features
Declarative routing
Nested routes
Dynamic route matching
Route guards
Browser and hash history support
2. Next.js
Next.js is a React framework growing in popularity
It provides server-side rendering and static site generation
The framework comes with a built-in routing based on the file system, making it simple and efficient to set up routes
Key Features
File-based routing
Server-side rendering
Static site generation
API routes
Code splitting and optimization
3. Reach Router
Reach Router was an alternative that focused on accessibility and simpler API
Currently, Reach Router has now been merged with React Router
It aimed to be a smaller and more lightweight alternative to React Router
Key Features
Simple API
Focus on accessibility
Nested routes
Dynamic route matching
4. Wouter
Wouter is a minimalist alternative to React Router
It is lightweight and provides a hook-based API for routing in React applications
Key Features
Hook-based API
Small bundle size
Simple and straightforward
No external dependencies
Comparison
React Router: More comprehensive, supports a wide range of features including route guards and nested routes. Suitable for complex applications
Next.js: Provides a framework for building entire applications with built-in routing, server-side rendering, and static site generation. Suitable for both small and large applications, especially those requiring SEO and performance optimization
Reach Router: Simpler and more lightweight, focused on accessibility. Suitable for smaller applications
Wouter: Extremely lightweight and minimalist, suitable for projects where simplicity and performance are priorities
Examples
Example 1: React Router
Basic Setup:
import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
const Home = () => <h2>Home</h2>;
const About = () => <h2>About</h2>;
const AppRouter = () => (
<Router>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
</nav>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</Switch>
</Router>
);
export default AppRouter;
Example 2: Nested Routes with React Router
Nested Routes Setup:
import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link, useRouteMatch } from 'react-router-dom';
const Home = () => <h2>Home</h2>;
const About = () => <h2>About</h2>;
const Topics = () => {
let { path, url } = useRouteMatch();
return (
<div>
<h2>Topics</h2>
<ul>
<li><Link to={`${url}/topic1`}>Topic 1</Link></li>
<li><Link to={`${url}/topic2`}>Topic 2</Link></li>
</ul>
<Switch>
<Route exact path={path} render={() => <h3>Please select a topic.</h3>} />
<Route path={`${path}/:topicId`} render={({ match }) => <h3>Selected Topic: {match.params.topicId}</h3>} />
</Switch>
</div>
);
};
const AppRouter = () => (
<Router>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
</nav>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</Switch>
</Router>
);
export default AppRouter;
Example 3: Next.js
File-Based Routing:
// pages/index.js
import Link from 'next/link';
const Home = () => (
<div>
<h1>Home</h1>
<Link href="/about">
<a>About</a>
</Link>
</div>
);
export default Home;
// pages/about.js
const About = () => <h1>About</h1>;
export default About;
Example 4: Wouter
Hook-Based Routing:
import React from 'react';
import { Router, Link, Route } from 'wouter';
const Home = () => <h2>Home</h2>;
const About = () => <h2>About</h2>;
const AppRouter = () => (
<Router>
<nav>
<ul>
<li><Link href="/">Home</Link></li>
<li><Link href="/about">About</Link></li>
</ul>
</nav>
<Route path="/" component={Home} />
<Route path="/about" component={About} />
</Router>
);
export default AppRouter;
Dynamic Routing and Route Guards
Dynamic Routing
Dynamic routing allows you to create routes that can handle dynamic parameters. This is useful for user profiles, articles, or any content that is dynamically generated.
Dynamic Route Example with React Router:
import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link, useParams } from 'react-router-dom';
const UserProfile = () => {
let { userId } = useParams();
return <h2>User Profile: {userId}</h2>;
};
const AppRouter = () => (
<Router>
<nav>
<ul>
<li><Link to="/user/1">User 1</Link></li>
<li><Link to="/user/2">User 2</Link></li>
</ul>
</nav>
<Switch>
<Route path="/user/:userId" component={UserProfile} />
</Switch>
</Router>
);
export default AppRouter;
Route Guards
Route guards are used to protect routes from being accessed by unauthorized users. They can be implemented using higher-order components or inline logic within the route definitions.
Route Guard Example with React Router:
import React from 'react';
import { BrowserRouter as Router, Route, Redirect, Switch } from 'react-router-dom';
const isAuthenticated = () => {
// Replace with actual authentication logic
return localStorage.getItem('authToken') !== null;
};
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
isAuthenticated() ? <Component {...props} /> : <Redirect to="/login" />
}
/>
);
const Home = () => <h2>Home</h2>;
const Login = () => <h2>Login</h2>;
const Dashboard = () => <h2>Dashboard</h2>;
const AppRouter = () => (
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/login" component={Login} />
<PrivateRoute path="/dashboard" component={Dashboard} />
</Switch>
</Router>
);
export default AppRouter;