@reach/router, react-router-dom, react-router-native, and wouter are all client-side routing libraries designed to manage navigation and URL-to-component mapping in React applications. react-router-dom is the standard, full-featured router for web applications, while react-router-native provides equivalent functionality for React Native mobile apps. wouter is a minimalist alternative focused on small bundle size and hook-based APIs. @reach/router was an earlier, accessibility-focused router that has since been deprecated in favor of React Router v6+.
Choosing the right routing library for a React application is a foundational architectural decision. The four packages under consideration — @reach/router, react-router-dom, react-router-native, and wouter — all solve the core problem of mapping URLs to UI components, but they differ significantly in scope, API design, platform support, and maintenance status. Let’s examine them through the lens of real-world frontend engineering.
Before diving into features, it’s critical to address project health:
@reach/router is officially deprecated. According to its npm page and GitHub repository, development has ceased, and users are strongly encouraged to migrate to react-router-dom v6+. Do not use @reach/router in new projects.react-router-dom and react-router-native are actively maintained as part of the unified React Router v6+ ecosystem.wouter is actively maintained with regular releases and a clear focus on minimalism.react-router-dom: The Full-Featured Web Routerreact-router-dom is the standard, full-featured routing solution for web applications built with React. It provides a rich set of primitives for nested routes, data loading (via loaders/actions in v6.4+), navigation guards, scroll restoration, and more. It’s designed for complex SPAs and hybrid SSR/SSG apps.
// react-router-dom v6
import { createBrowserRouter, RouterProvider, Outlet } from 'react-router-dom';
const router = createBrowserRouter([
{
path: '/',
element: <Layout><Outlet /></Layout>,
children: [
{ index: true, element: <Home /> },
{ path: 'users/:id', element: <User /> }
]
}
]);
function App() {
return <RouterProvider router={router} />;
}
react-router-native: React Native Counterpartreact-router-native brings the same mental model and APIs of React Router to React Native apps. It integrates with native navigation stacks and handles deep linking. If you’re already using React Router on the web and want consistency across platforms, this is the natural choice.
// react-router-native
import { NativeRouter, Routes, Route } from 'react-router-native';
import { View } from 'react-native';
function App() {
return (
<NativeRouter>
<View style={{ flex: 1 }}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</View>
</NativeRouter>
);
}
wouter: Minimalist Alternativewouter is a tiny (~1.5kB) hook-based router that avoids context and complex abstractions. It’s ideal for small to medium apps where bundle size matters and advanced features like nested routes or data loading aren’t needed. It uses hooks exclusively and works well with modern React patterns.
// wouter
import { BrowserRouter, Route, Link, useLocation } from 'wouter';
function App() {
return (
<BrowserRouter>
<nav>
<Link href="/">Home</Link>
<Link href="/about">About</Link>
</nav>
<Route path="/" component={Home} />
<Route path="/about" component={About} />
</BrowserRouter>
);
}
@reach/router: Deprecated SimplicityWhile once praised for its clean, accessibility-focused API, @reach/router is no longer maintained. Its syntax was intuitive but lacked the scalability of modern routers.
// @reach/router (deprecated)
import { Router, Link } from '@reach/router';
function App() {
return (
<Router>
<Link to="/">Home</Link>
<Link to="/dashboard">Dashboard</Link>
<Home path="/" />
<Dashboard path="/dashboard" />
</Router>
);
}
react-router-dom supports deeply nested routes with layout nesting out of the box.// react-router-dom: Nested layouts
const router = createBrowserRouter([
{
element: <AuthLayout><Outlet /></AuthLayout>,
children: [
{ path: 'login', element: <Login /> },
{ path: 'signup', element: <Signup /> }
]
}
]);
wouter requires manual composition for layouts since it doesn’t support route nesting.// wouter: Manual layout handling
function AuthRoutes() {
const [location] = useLocation();
return (
<AuthLayout>
{location === '/login' ? <Login /> : <Signup />}
</AuthLayout>
);
}
react-router-native mirrors react-router-dom’s nesting capabilities but adapts to native navigation paradigms.// react-router-native: Nested routes
<Routes>
<Route element={<TabLayout><Outlet /></TabLayout>}>
<Route path="feed" element={<Feed />} />
<Route path="profile" element={<Profile />} />
</Route>
</Routes>
@reach/router used implicit nesting via component hierarchy, but this approach didn’t scale well.// @reach/router: Implicit nesting (deprecated)
<Router>
<App path="/">
<Feed path="/feed" />
<Profile path="/profile" />
</App>
</Router>
All libraries support programmatic navigation, but with different ergonomics:
react-router-dom: Uses useNavigate() hook.
const navigate = useNavigate();
navigate('/users/123');
react-router-native: Same useNavigate() API.
const navigate = useNavigate();
navigate('/profile');
wouter: Uses useLocation() to read/write location.
const [, setLocation] = useLocation();
setLocation('/about');
@reach/router: Used navigate() function (now obsolete).
import { navigate } from '@reach/router';
navigate('/old-path'); // Don't use this
This is a key differentiator:
react-router-dom for web browsers.react-router-native for React Native mobile apps.wouter only for lightweight web apps where you don’t need native integration or advanced routing features.@reach/router entirely in new codebases.Only react-router-dom (v6.4+) offers built-in data loaders and actions, enabling integrated data fetching, mutations, and pending states without external state management:
// react-router-dom: Loader
const router = createBrowserRouter([
{
path: '/users/:id',
element: <UserPage />,
loader: ({ params }) => fetchUser(params.id)
}
]);
// In component
const user = useLoaderData();
Neither wouter nor react-router-native provide this capability. You’d need to layer on useEffect, SWR, or React Query for data fetching.
react-router-dom includes dev tools support and clear error messages.wouter’s simplicity makes it easy to mock in tests (just mock useLocation).react-router-native integrates with React Native debugging tools.@reach/router lacks modern debugging aids due to deprecation.| Scenario | Recommended Package |
|---|---|
| New web app needing SSR, nested routes, or data loading | react-router-dom |
| React Native app requiring deep linking | react-router-native |
| Small static site or PWA with basic routing | wouter |
Legacy project still on @reach/router | Migrate to react-router-dom |
For most professional web projects today, react-router-dom is the default choice — it’s mature, well-documented, and scales from simple blogs to enterprise dashboards. If you’re building for React Native, react-router-native gives you API parity. Only consider wouter if you’ve measured your bundle and confirmed that React Router’s footprint is unacceptable — and you’re willing to trade off features for bytes. And remember: never start a new project with @reach/router.
Choose react-router-dom for web applications that require robust routing features like nested layouts, data loaders, scroll restoration, or integration with server-rendered architectures. It’s the industry standard for complex SPAs and full-stack React apps, offering long-term stability and comprehensive documentation.
Choose wouter for lightweight web projects where bundle size is critical and advanced routing features (like nested routes or built-in data loading) aren’t needed. Its hook-centric API works well with modern React patterns, but you’ll need to handle layout composition and data fetching manually.
Do not use @reach/router in new projects — it is officially deprecated, no longer maintained, and lacks compatibility with modern React patterns. If you're maintaining a legacy codebase using it, plan a migration to react-router-dom v6+ as soon as feasible.
Choose react-router-native when building React Native applications that need consistent routing logic with your web counterpart or require deep linking and native navigation integration. It shares the same core concepts as react-router-dom, making cross-platform code sharing easier.
This package simply re-exports everything from react-router to smooth the upgrade path for v6 applications. Once upgraded you can change all of your imports and remove it from your dependencies:
-import { Routes } from "react-router-dom"
+import { Routes } from "react-router"