useRoute - greydragon888/real-router GitHub Wiki
-
Name:
useRoute - Purpose: Get current route state (route, previousRoute) and navigator instance for navigation
- When to use: When a component needs access to current route, parameters, or navigation
function useRoute<P extends Params = Params>(): RouteContext<P>;import { useRoute } from "@real-router/react";
// or
import { useRoute } from "@real-router/preact";
// or
import { useRoute } from "@real-router/solid";
// or
import { useRoute } from "@real-router/vue";
// or
import { useRoute } from "@real-router/svelte";
// Angular
import { injectRoute } from "@real-router/angular";Solid.js note: In Solid,
useRoute()returns reactive state asAccessor<RouteContext>. Call the accessor to read:const routeState = useRoute(); routeState().route?.nameVue note: In Vue,
useRoute()returns{ navigator, route: ShallowRef, previousRoute: ShallowRef }. Access refs via.value:const { route } = useRoute(); route.value?.nameAngular note:
injectRoute()returnsRouteSignals = { routeState: Signal<RouteSnapshot>, navigator }. Read the signal in templates:route.routeState().route?.name. Unlike React/Preact which return plain values, Angular returns a signal that must be called.
function MyComponent() {
const { route, previousRoute, navigator } = useRoute();
return <div>Current route: {route?.name}</div>;
}Hook accepts no runtime parameters. Accepts one optional generic type parameter:
| Generic | Default | Description |
|---|---|---|
P |
Params |
Shape of route.params — propagates into RouteContext<P> / RouteState<P> / State<P>
|
Runtime is unchanged — the generic is erased at compile time and the cast happens once inside the hook body instead of at every call site.
type SearchParams = { q: string; sort: string; dir: string } & Params;
const { route } = useRoute<SearchParams>();
route?.params.q; // typed as string — no cast needed
route?.params.sort; // typed as stringSupported across every adapter:
| Adapter | Call |
|---|---|
| React |
const { route } = useRoute<SearchParams>(); → route?.params.q
|
| Preact |
const { route } = useRoute<SearchParams>(); → route?.params.q
|
| Solid |
const state = useRoute<SearchParams>(); → state().route?.params.q
|
| Vue |
const { route } = useRoute<SearchParams>(); → route.value?.params.q
|
| Svelte |
const { route } = useRoute<SearchParams>(); → route.current?.params.q
|
| Angular |
const r = injectRoute<SearchParams>(); → r.routeState().route?.params.q
|
-
Type:
RouteContext<P>(wherePdefaults toParams) - Description: Object with current navigator, route, and previous route
| Field | Type | Description |
|---|---|---|
navigator |
Navigator |
Navigator instance for navigation and state access |
route |
State<P> | undefined |
Current route state (name, params, path) with typed params
|
previousRoute |
State | undefined |
Previous route state (kept loose — may belong to a different route) |
interface State {
name: string; // Route name (e.g.: "users.profile")
params: Params; // Route parameters
path: string; // URL path
}| Provider | Required | Description |
|---|---|---|
RouterProvider |
Yes | Must wrap the component using the hook |
-
useContext(RouteContext)— getting context from provider
-
Reference stability:
navigator— stable reference.routeandpreviousRoute— new objects on each navigation - Update triggers: Component re-renders on every successful router navigation
-
Memoization: Use
useMemofor computations based onroute
const userName = useMemo(() => route?.params?.name, [route?.params?.name]);| Condition | Error | How to Avoid |
|---|---|---|
Hook called outside RouterProvider
|
"useRoute must be used within a RouteProvider" |
Wrap component in RouterProvider
|
- Returns navigator instance via
result.current.navigator - Returns current route via
result.current.route - Automatically updates on navigation (
navigator.navigate())
- If router not started,
routemay beundefined - On first navigation
previousRoutewill beundefined
- Hook throws error if no provider (fail-fast)
- State synchronized with router via
useSyncExternalStore
| Hook | When to Use Instead |
|---|---|
useRouter |
When you only need router instance without subscription |
useRouteNode |
When you need subscription only to specific route node |
import { useRoute } from "@real-router/react";
function CurrentPage() {
const { route } = useRoute();
if (!route) {
return <div>Loading...</div>;
}
return (
<div>
<h1>Page: {route.name}</h1>
<p>Path: {route.path}</p>
</div>
);
}function Navigation() {
const { navigator, route } = useRoute();
const goToProfile = () => {
navigator.navigate("users.profile", { id: "123" });
};
return (
<nav>
<span>Current: {route?.name}</span>
<button onClick={goToProfile}>Go to Profile</button>
</nav>
);
}function ConditionalContent() {
const { route } = useRoute();
if (route?.name === "admin") {
return <AdminPanel />;
}
return <UserDashboard />;
}import type { Params } from "@real-router/core";
type SearchParams = { q: string; sort: string } & Params;
function SearchPage() {
const { route } = useRoute<SearchParams>();
const q = route?.params.q ?? ""; // string — no `as` cast
const sort = route?.params.sort ?? ""; // string
return <Results query={q} sort={sort} />;
}Before the generic, the same code required as casts:
const { route } = useRoute();
const q = (route?.params.q as string) ?? ""; // unsafe cast
const sort = (route?.params.sort as string) ?? ""; // unsafe castType generation will later automate this inference; the generic provides a manual opt-in available today.
// Don't create subscription manually
function Bad() {
const { navigator } = useRoute();
useEffect(() => {
navigator.subscribe(() => {
/* ... */
}); // Duplication!
}, []);
}
// Use values from hook directly
function Good() {
const { route } = useRoute(); // Already subscribed via RouterProvider
return <div>{route?.name}</div>;
}// Don't extract router to pass to deep components
function Bad() {
const { navigator } = useRoute();
return <DeepChild navigator={navigator} />; // Props drilling
}
// Use useRoute/useRouter in child component
function Good() {
return <DeepChild />; // Child will call useRoute() itself
}Comparison of
@real-router/react/useRoutewithreact-router5/useRoutefrom router5.
| router5 (react-router5) | Real Router (@real-router/react) | |
|---|---|---|
| Export | export default function useRoute() |
export const useRoute |
| Signature | useRoute(): RouteContext |
useRoute(): RouteContext |
Return type route |
State |
State | undefined |
Return type previousRoute |
State | null |
State | undefined |
Return field router |
{ router: Router, ... } |
Replaced by navigator: Navigator
|
| Severity | What Changed | Was | Now | Impact |
|---|---|---|---|---|
| CRITICAL | Export type | export default |
export const (named) |
All imports will break |
| HIGH | Context validation | Returns undefined if no provider |
Throws Error
|
Code without provider will crash |
| MEDIUM |
route type |
State (always defined) |
State | undefined |
TypeScript will require undefined checks |
| MEDIUM |
previousRoute type |
State | null |
State | undefined |
Need to replace null checks with undefined
|
| HIGH | Return field router
|
router: Router in return value |
navigator: Navigator |
Update destructuring and method calls |
Hook accepts no parameters — no changes.
| Field | Change | Migration |
|---|---|---|
route |
Type State → State | undefined
|
Add if (route) check or use optional chaining |
previousRoute |
Type State | null → State | undefined
|
Replace !== null with !== undefined or use truthiness check |
router |
Field removed — replaced by navigator: Navigator
|
Use navigator instead; it provides navigate, getState, subscribe
|
// Code that will break (import)
import useRoute from "react-router5";
// Code after migration
import { useRoute } from "@real-router/react";// Code that will break (types)
const { route } = useRoute();
console.log(route.name); // TS error: route may be undefined
// Code after migration
const { route } = useRoute();
console.log(route?.name); // Optional chaining
// or
if (route) {
console.log(route.name);
}// Code that will break (previousRoute)
if (previousRoute !== null) {
/* ... */
}
// Code after migration
if (previousRoute !== undefined) {
/* ... */
}
// or simply
if (previousRoute) {
/* ... */
}-
Fail-fast validation: Hook now throws clear error
"useRoute must be used within a RouteProvider"if called outside provider. Previously returnedundefined, causing implicit errors later.
-
Nullable → Optional: Transition from
nulltoundefinedfor missing values follows idiomatic TypeScript
| Dependency | Was | Now |
|---|---|---|
| Router package | router5 |
@real-router/core |
| React bindings | react-router5 |
@real-router/react |
- Replace
import useRoute from 'react-router5'withimport { useRoute } from '@real-router/react' - Add
undefinedchecks forroute(or use?.) - Replace
!== nullchecks with!== undefinedforpreviousRoute - Ensure component is wrapped in
RouterProvider(otherwise will error)
- Update imports: Replace default import with named import
-
Update types: Add
undefinedhandling forroute -
Update null checks: Replace with
undefinedor truthiness checks -
Check providers: Ensure all components with
useRouteare insideRouterProvider
| Category | Status |
|---|---|
| Breaking Changes | CRITICAL (export) |
| New parameters | None |
| Removed parameters | None |
| Behavior changes | Fail-fast validation |
| Optimizations | None |
Maximum severity: CRITICAL — all import changes required