React Router Cheatsheet

Location and Search Params

Use this React Router reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

useLocation

import { useLocation } from "react-router";

const location = useLocation();
FieldExampleNotes
pathname/concerts/parisNo search, no hash, includes basename
search?page=2&sort=ascLeading ? included, empty string if none
hash#lineupLeading # included
state{ from: "/checkout" }Set by Link state or navigate
key"ab12cd"Unique per history entry, "default" at start

location.state lives in the history entry, not the URL. It survives back and forward, it does not survive a hard reload or a shared link, and it is never available on the server, so treat it as a hint and not as data.

Search Params in the Client

import { useSearchParams } from "react-router";

const [searchParams, setSearchParams] = useSearchParams();

const q = searchParams.get("q") ?? "";
const tags = searchParams.getAll("tag");
const page = Number(searchParams.get("page") ?? 1);

// functional update, keeps the params you do not touch
setSearchParams(
  (prev) => {
    prev.set("page", String(page + 1));
    return prev;
  },
  { replace: true, preventScrollReset: true }
);

setSearchParams is a navigation. Without { replace: true } every keystroke in a filter input pushes a history entry, and the back button becomes useless.

URLSearchParams methodUse
get(k)First value or null
getAll(k)Every value, for checkbox groups
has(k)Presence check, has(k, v) checks a pair
set(k, v)Replace all values for k
append(k, v)Add another value for k
delete(k)Remove all values for k
toString()Serialize without the ?
sort()Stable ordering, handy for cache keys
entries()Iterate pairs

Search Params on the Server

A loader gets the raw Request, so parse the URL there. This is the read that matters in Framework mode, because it runs before render and can be cached.

import type { Route } from "./+types/search";

export async function loader({ request }: Route.LoaderArgs) {
  const url = new URL(request.url);
  const q = url.searchParams.get("q") ?? "";
  const page = Number(url.searchParams.get("page") ?? 1);
  return { results: await search(q, page), q, page };
}

v8 also passes a normalized url argument alongside request, stripped of the router's own .data suffix and internal search params. Prefer it when you only need the URL.

Read search params withWhen
A loader plus new URL(request.url)The value changes what you fetch
useSearchParamsThe value only changes what you render
BothServer filters the list, client toggles a view mode

Use a <Form method="get"> for a filter form and the params write themselves from the input names, with no onChange handler at all.

Path Utilities

UtilityReturns
useHref(to)The full href a Link would produce, basename included
useResolvedPath(to)A Path object for a relative to
generatePath(pattern, params)Fills :params into a pattern string
matchPath(pattern, pathname)A match with params, or null
matchRoutes(routes, location)The matched branch, for custom logic
resolvePath(to, from)Resolve one path against another
href(path, params)Framework mode, type safe from the route config
import { generatePath, matchPath, href } from "react-router";

generatePath("/users/:id/posts/:postId", { id: "7", postId: "3" });
// "/users/7/posts/3"

matchPath("/users/:id", "/users/7");
// { params: { id: "7" }, pathname: "/users/7", pattern: {...} }

// href is checked against routes.ts, a typo is a type error
<Link to={href("/products/:id", { id: "abc123" })}>Product</Link>

href percent encodes param values for a path segment and preserves the slashes inside a splat value.

Blocking Navigation

import { unstable_usePrompt, useBlocker } from "react-router";

// simple confirm dialog, still unstable_ in v8
unstable_usePrompt({
  when: isDirty,
  message: "Leave without saving?",
});

// custom UI
const blocker = useBlocker(
  ({ currentLocation, nextLocation }) =>
    isDirty && currentLocation.pathname !== nextLocation.pathname
);

if (blocker.state === "blocked") {
  return (
    <dialog open>
      <button onClick={() => blocker.proceed()}>Discard</button>
      <button onClick={() => blocker.reset()}>Stay</button>
    </dialog>
  );
}

Blockers only cover client side navigations. For a tab close or a reload you need useBeforeUnload, and the browser shows its own generic message.

Hash Routing

Hash routing is a Declarative or Data mode setup for static hosts that cannot rewrite every path to index.html.

import { HashRouter } from "react-router";
import { createHashRouter } from "react-router";

<HashRouter>{/* routes */}</HashRouter>
const router = createHashRouter(routes);

Framework mode has no hash router. If you need a fully static deploy, use ssr: false with prerender in react-router.config.ts and keep real paths, which is better for crawlers anyway.