useRoute - greydragon888/real-router GitHub Wiki

useRoute

1. Overview

  • 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

2. Signature

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 as Accessor<RouteContext>. Call the accessor to read: const routeState = useRoute(); routeState().route?.name

Vue note: In Vue, useRoute() returns { navigator, route: ShallowRef, previousRoute: ShallowRef }. Access refs via .value: const { route } = useRoute(); route.value?.name

Angular note: injectRoute() returns RouteSignals = { 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.

Basic Example

function MyComponent() {
  const { route, previousRoute, navigator } = useRoute();

  return <div>Current route: {route?.name}</div>;
}

3. Parameters

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 string

Supported 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

4. Return Value

  • Type: RouteContext<P> (where P defaults to Params)
  • Description: Object with current navigator, route, and previous route

Return Value Structure

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)

route Structure

interface State {
  name: string; // Route name (e.g.: "users.profile")
  params: Params; // Route parameters
  path: string; // URL path
}

5. Dependencies and Context

Required Providers

Provider Required Description
RouterProvider Yes Must wrap the component using the hook

Used Hooks

  • useContext(RouteContext) — getting context from provider

6. Re-render Behavior

  • Reference stability: navigator — stable reference. route and previousRoute — new objects on each navigation
  • Update triggers: Component re-renders on every successful router navigation
  • Memoization: Use useMemo for computations based on route
const userName = useMemo(() => route?.params?.name, [route?.params?.name]);

7. Possible Errors

Condition Error How to Avoid
Hook called outside RouterProvider "useRoute must be used within a RouteProvider" Wrap component in RouterProvider

8. Behavior

Main Scenarios

  • Returns navigator instance via result.current.navigator
  • Returns current route via result.current.route
  • Automatically updates on navigation (navigator.navigate())

Edge Cases

  • If router not started, route may be undefined
  • On first navigation previousRoute will be undefined

Guarantees

  • Hook throws error if no provider (fail-fast)
  • State synchronized with router via useSyncExternalStore

9. Related Hooks

Hook When to Use Instead
useRouter When you only need router instance without subscription
useRouteNode When you need subscription only to specific route node

10. Usage Examples

Basic Example

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

Navigation from Component

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

Conditional Rendering by Route

function ConditionalContent() {
  const { route } = useRoute();

  if (route?.name === "admin") {
    return <AdminPanel />;
  }

  return <UserDashboard />;
}

Typed route.params Without Casts

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 cast

Type generation will later automate this inference; the generic provides a manual opt-in available today.

Anti-patterns

// 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
}

11. Migration from router5

Comparison of @real-router/react/useRoute with react-router5/useRoute from router5.

Version Comparison

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

1. Breaking Changes

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

Parameter Changes

Hook accepts no parameters — no changes.

Return Value Changes

Field Change Migration
route Type StateState | undefined Add if (route) check or use optional chaining
previousRoute Type State | nullState | undefined Replace !== null with !== undefined or use truthiness check
router Field removed — replaced by navigator: Navigator Use navigator instead; it provides navigate, getState, subscribe

Examples

// 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) {
  /* ... */
}

2. Implementation Changes

New Behavior

  • Fail-fast validation: Hook now throws clear error "useRoute must be used within a RouteProvider" if called outside provider. Previously returned undefined, causing implicit errors later.

Type Changes

  • Nullable → Optional: Transition from null to undefined for missing values follows idiomatic TypeScript

3. Dependency Changes

Dependency Was Now
Router package router5 @real-router/core
React bindings react-router5 @real-router/react

4. Migration Guide

Checklist

  • Replace import useRoute from 'react-router5' with import { useRoute } from '@real-router/react'
  • Add undefined checks for route (or use ?.)
  • Replace !== null checks with !== undefined for previousRoute
  • Ensure component is wrapped in RouterProvider (otherwise will error)

Step-by-Step Migration

  1. Update imports: Replace default import with named import
  2. Update types: Add undefined handling for route
  3. Update null checks: Replace with undefined or truthiness checks
  4. Check providers: Ensure all components with useRoute are inside RouterProvider

5. Summary

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

⚠️ **GitHub.com Fallback** ⚠️