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.

Location

import { useLocation } from "react-router";

function Analytics() {
  const location = useLocation();
  // { pathname, search, hash, state, key }
  useEffect(() => {
    track(location.pathname + location.search);
  }, [location]);
}
FieldIs
pathname/users/42
search?tab=posts including the ?
hash#comments including the #
stateWhatever you passed to state, not in the URL
keyA unique key per history entry, useful for scroll restoration

state survives back and forward navigation but not a page reload or a shared link, so never put anything a user might need to bookmark in it.

Search Params

import { useSearchParams } from "react-router";

function Search() {
  const [searchParams, setSearchParams] = useSearchParams();

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

  return (
    <input
      value={q}
      onChange={(e) => {
        setSearchParams((prev) => {
          const next = new URLSearchParams(prev);
          if (e.target.value) next.set("q", e.target.value);
          else next.delete("q");
          next.delete("page");            // reset pagination on a new query
          return next;
        }, { replace: true });
      }}
    />
  );
}

searchParams is a standard URLSearchParams, so get, getAll, set, append, delete, and toString all behave normally. Pass { replace: true } for as-you-type filters so each keystroke does not become a history entry.

Putting filter state in the URL rather than in component state is the point: the view becomes shareable, bookmarkable, and survives a refresh for free.

Scroll Restoration

import { ScrollRestoration } from "react-router";

function Root() {
  return (
    <>
      <Outlet />
      <ScrollRestoration />
    </>
  );
}

// Custom key: share a scroll position across a set of URLs
<ScrollRestoration
  getKey={(location) => location.pathname}
/>

Framework mode includes this in the root route template. In data mode you add it once, near the bottom of your root layout.

Blocking Navigation

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

function Editor({ isDirty }) {
  const blocker = useBlocker(
    ({ currentLocation, nextLocation }) =>
      isDirty && currentLocation.pathname !== nextLocation.pathname
  );

  if (blocker.state === "blocked") {
    return (
      <div role="dialog" aria-modal="true">
        <p>You have unsaved changes.</p>
        <button onClick={() => blocker.proceed()}>Leave</button>
        <button onClick={() => blocker.reset()}>Stay</button>
      </div>
    );
  }
}

useBlocker only blocks client-side navigations. To catch a tab close or a reload you still need a beforeunload listener, and browsers will only show their own generic message there.