react-router-dom vs wouter vs @reach/router vs react-router-native
Client-Side Routing Solutions for React Applications
react-router-domwouter@reach/routerreact-router-nativeSimilar Packages:
Client-Side Routing Solutions for React Applications

@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+.

Npm Package Weekly Downloads Trend
3 Years
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
react-router-dom17,796,47956,0405.46 kB146a day agoMIT
wouter684,8947,65774.8 kB249 hours agoUnlicense
@reach/router491,7926,865-1735 years agoMIT
react-router-native19,46656,04039.9 kB14610 months agoMIT

React Routing Libraries Compared: @reach/router, react-router-dom, react-router-native, and wouter

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.

⚠️ Deprecation Status and Maintenance

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.

🧭 Core Philosophy and Use Case Fit

react-router-dom: The Full-Featured Web Router

react-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 Counterpart

react-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 Alternative

wouter 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 Simplicity

While 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>
  );
}

🔍 Feature Comparison: Nested Routes and Layouts

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>

🔄 Navigation and Programmatic Control

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
    

📱 Platform Targeting

This is a key differentiator:

  • Use react-router-dom for web browsers.
  • Use react-router-native for React Native mobile apps.
  • Use wouter only for lightweight web apps where you don’t need native integration or advanced routing features.
  • Avoid @reach/router entirely in new codebases.

🛠️ Data Loading and Advanced Patterns

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.

🧪 Testing and Debugging

  • 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.

✅ When to Use Which

ScenarioRecommended Package
New web app needing SSR, nested routes, or data loadingreact-router-dom
React Native app requiring deep linkingreact-router-native
Small static site or PWA with basic routingwouter
Legacy project still on @reach/routerMigrate to react-router-dom

💡 Final Guidance

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.

How to Choose: react-router-dom vs wouter vs @reach/router vs react-router-native
  • react-router-dom:

    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.

  • wouter:

    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.

  • @reach/router:

    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.

  • react-router-native:

    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.

README for react-router-dom

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"