@real-router/react
v0.14.9
Published
React integration for Real-Router
Maintainers
Readme
@real-router/react
React integration for Real-Router — hooks, components, and context providers.
Installation
npm install @real-router/react @real-router/core @real-router/browser-pluginPeer dependency: react >= 18.0.0
Entry Points
| Import Path | React Version | Includes |
| --------------------------- | ------------- | ------------------------------------------------------ |
| @real-router/react | 19.2+ | Full API (hooks, Link, RouteView with keepAlive) |
| @real-router/react/legacy | 18+ | All hooks and Link, no RouteView |
Both share the same underlying code — /legacy excludes components that require React 19.2's <Activity> API.
Quick Start
import { createRouter } from "@real-router/core";
import { browserPluginFactory } from "@real-router/browser-plugin";
import { RouterProvider, RouteView, Link } from "@real-router/react";
const router = createRouter([
{ name: "home", path: "/" },
{
name: "users",
path: "/users",
children: [{ name: "profile", path: "/:id" }],
},
]);
router.usePlugin(browserPluginFactory());
router.start();
function App() {
return (
<RouterProvider router={router}>
<nav>
<Link routeName="home">Home</Link>
<Link routeName="users">Users</Link>
</nav>
<RouteView nodeName="">
<RouteView.Match routeName="home">
<HomePage />
</RouteView.Match>
<RouteView.Match routeName="users">
<UsersPage />
</RouteView.Match>
<RouteView.NotFound>
<NotFoundPage />
</RouteView.NotFound>
</RouteView>
</RouterProvider>
);
}Hooks
| Hook | Returns | Re-renders |
| ----------------------- | ----------------------------------------- | --------------------------------------- |
| useRouter() | Router | Never |
| useNavigator() | Navigator | Never (stable ref, safe to destructure) |
| useRoute() | { router, route, previousRoute } | Every navigation |
| useRouteNode(name) | { router, route, previousRoute } | Only when node activates/deactivates |
| useRouteUtils() | RouteUtils | Never |
| useRouterTransition() | { isTransitioning, toRoute, fromRoute } | On transition start/end |
// useRouteNode — re-renders only when "users.*" changes
function UsersLayout() {
const { route } = useRouteNode("users");
if (!route) return null;
switch (route.name) {
case "users":
return <UsersList />;
case "users.profile":
return <UserProfile id={route.params.id} />;
default:
return null;
}
}
// useNavigator — stable reference, never causes re-renders
function BackButton() {
const navigator = useNavigator();
return <button onClick={() => navigator.navigate("home")}>Back</button>;
}
// useRouterTransition — progress bars, loading states
function GlobalProgress() {
const { isTransitioning } = useRouterTransition();
if (!isTransitioning) return null;
return <div className="progress-bar" />;
}Components
<Link>
Navigation link with automatic active state detection. Re-renders only when its active status changes.
<Link
routeName="users.profile"
routeParams={{ id: "123" }}
activeClassName="active" // default: "active"
activeStrict={false} // default: false (ancestor match)
ignoreQueryParams={true} // default: true
routeOptions={{ replace: true }}
>
View Profile
</Link><RouteView> (React 19.2+)
Declarative route matching with optional keepAlive — preserves component state via React's <Activity> API.
<RouteView nodeName="">
<RouteView.Match segment="users" keepAlive>
<UsersPage /> {/* State preserved when navigating away */}
</RouteView.Match>
<RouteView.Match segment="settings">
<SettingsPage /> {/* Unmounts normally */}
</RouteView.Match>
<RouteView.NotFound>
<NotFoundPage />
</RouteView.NotFound>
</RouteView>RouteView.Match props
| Prop | Type | Description |
| ----------- | ----------- | --------------------------------------------------------------------------- |
| segment | string | Route segment to match |
| keepAlive | boolean | Preserve state via React <Activity> (React 19.2+) |
| fallback | ReactNode | Shown while children suspend. Wraps children in <Suspense> when provided. |
Lazy loading with fallback
Pass fallback to code-split a route component. RouteView.Match wraps children in <Suspense> automatically:
import { lazy } from "react";
const LazyDashboard = lazy(() => import("./Dashboard"));
<RouteView nodeName="">
<RouteView.Match segment="dashboard" fallback={<Spinner />}>
<LazyDashboard />
</RouteView.Match>
</RouteView>;fallback and keepAlive work together — <Activity> wraps the whole match including the <Suspense> boundary.
<RouterErrorBoundary>
Declarative error handling for navigation errors. Shows a fallback alongside children (not instead of) when a guard rejects or a route is not found.
<RouterErrorBoundary
fallback={(error, resetError) => (
<div className="toast">
{error.code} <button onClick={resetError}>Dismiss</button>
</div>
)}
onError={(error) => analytics.track("nav_error", { code: error.code })}
>
<Link routeName="protected">Go to Protected</Link>
</RouterErrorBoundary>Auto-resets on next successful navigation. Works with both <Link> and imperative router.navigate().
Available from both @real-router/react and @real-router/react/legacy.
React 18 Migration
One import path change — all hooks and Link work identically:
- import { useRouteNode, Link } from '@real-router/react';
+ import { useRouteNode, Link } from '@real-router/react/legacy';RouteView is not available from /legacy. Use useRouteNode with a switch/case pattern instead.
Migration from react-router5
| API | react-router5 | @real-router/react |
| ------------------------------------------------------ | ------------- | ------------------ |
| RouterProvider, Link | Yes | Yes |
| useRouter, useRoute, useRouteNode | Yes | Yes |
| RouteView with keepAlive | No | Yes (React 19.2+) |
| useNavigator, useRouteUtils, useRouterTransition | No | Yes |
| RouterErrorBoundary (declarative error handling) | No | Yes |
| withRouter, withRoute, routeNode (HOCs) | Yes | No — use hooks |
| Router, Route, RouteNode (render props) | Yes | No — use hooks |
Accessibility
Enable screen reader announcements for route changes:
<RouterProvider router={router} announceNavigation>
{/* Your app */}
</RouterProvider>When enabled, a visually hidden aria-live region announces each navigation. Focus moves to the first <h1> on the new page. See Accessibility guide for details.
Documentation
Full documentation: Wiki
- RouterProvider · RouteView · RouterErrorBoundary · Link
- useRouter · useRoute · useRouteNode · useNavigator · useRouteUtils · useRouterTransition
Examples
14 runnable examples — each is a standalone Vite app. Run: cd examples/react/basic && pnpm dev
basic · nested-routes · auth-guards · data-loading · lazy-loading · async-guards · hash-routing · persistent-params · error-handling · dynamic-routes · keep-alive · legacy-entry · hmr · combined
Related Packages
| Package | Description |
| ---------------------------------------------------------------------------------------- | ------------------------------------ |
| @real-router/core | Core router (required dependency) |
| @real-router/browser-plugin | Browser History API integration |
| @real-router/sources | Subscription layer (used internally) |
| @real-router/route-utils | Route tree queries (useRouteUtils) |
Contributing
See contributing guidelines for development setup and PR process.
