URL Params
Route params are the URL segments React Router captures and hands to your component. Use them for resource ids and stable identifiers; use search params (URLSearchParams) for filters and pagination state. The two have very different semantics: route params change the resource, search params change the view.
useParams, useSearchParams, typed, and the common pitfalls
EXAMPLE
import { useParams, useSearchParams, useNavigate, Link } from 'react-router-dom';
import { useMemo } from 'react';
// 1) Route param — :id in the route definition
// route: { path: '/orders/:id', element: <OrderDetail /> }
function OrderDetail() {
const { id } = useParams<{ id: string }>(); // string | undefined
if (!id) return <p>Missing order id.</p>;
// Treat the param as opaque text; validate before using
if (!/^[a-f0-9-]{8,36}$/.test(id)) return <p>Bad order id.</p>;
return <p>Order {id}</p>;
}
// 2) Multiple route params
// route: { path: '/users/:userId/orders/:orderId', element: <UserOrder /> }
function UserOrder() {
const { userId, orderId } = useParams<{ userId: string; orderId: string }>();
return <p>{userId} -> {orderId}</p>;
}
// 3) Optional + wildcard params (v6 syntax)
// route: { path: '/files/*', element: <FilePath /> }
function FilePath() {
const { '*': rest } = useParams(); // matches the splat
return <p>{rest}</p>;
}
// 4) Search params — for filters, sort, pagination
// /search?q=jacket&sort=newest&page=2
function Search() {
const [params, setParams] = useSearchParams();
const q = params.get('q') ?? '';
const sort = params.get('sort') ?? 'newest';
const page = Number(params.get('page') ?? '1');
function update(next: Record<string, string>) {
const merged = new URLSearchParams(params);
Object.entries(next).forEach(([k, v]) => v ? merged.set(k, v) : merged.delete(k));
setParams(merged); // updates the URL + triggers re-render
}
return (
<div>
<input
value={q}
onChange={(e) => update({ q: e.target.value, page: '1' })}
placeholder='Search'
/>
<select value={sort} onChange={(e) => update({ sort: e.target.value })}>
<option value='newest'>Newest</option>
<option value='price'>Price</option>
</select>
<p>Page {page}</p>
<button onClick={() => update({ page: String(page + 1) })}>Next</button>
</div>
);
}
// 5) Memoise derived values from search params
function FilteredList({ products }: { products: any[] }) {
const [params] = useSearchParams();
const sort = params.get('sort') ?? 'newest';
const sorted = useMemo(() => {
const copy = [...products];
if (sort === 'price') copy.sort((a, b) => a.priceCents - b.priceCents);
if (sort === 'newest') copy.sort((a, b) => +new Date(b.createdAt) - +new Date(a.createdAt));
return copy;
}, [products, sort]);
return <ul>{sorted.map((p) => <li key={p.id}>{p.name}</li>)}</ul>;
}
// 6) Typed route helpers — generate URLs and props in one place
// Avoids 'magic strings' scattered across the app.
const Routes = {
orderDetail: (id: string) => '/orders/' + encodeURIComponent(id),
search: (q: string, sort = 'newest') =>
\`/search?q=${encodeURIComponent(q)}&sort=${sort}\`,
};
function NavLinks() {
return (
<nav>
<Link to={Routes.orderDetail('o-1')}>Order o-1</Link>
<Link to={Routes.search('jacket', 'price')}>Search jackets</Link>
</nav>
);
}
// 7) Programmatic navigation with state
function ToCheckout({ orderId }: { orderId: string }) {
const navigate = useNavigate();
return (
<button onClick={() => navigate(Routes.orderDetail(orderId), { state: { from: 'cart' } })}>
Checkout
</button>
);
}
// 8) Pitfalls
// - Trusting useParams to return non-empty -> always check
// - Encoding: never embed raw user text in URLs; use encodeURIComponent / Routes helpers
// - useSearchParams returns the SAME object on re-render; mutate via new URLSearchParams
// - Treating search params as the source of truth across multiple components ->
// one component owns 'updateParams'; others read
// 9) Decision: route params vs search params
// route param -> identifies the resource (id, slug)
// search param -> shape of the view (filters, sort, pagination)
// route state -> ephemeral data the next screen needs but should not appear in URL
Why it matters
Use route params for resource ids and search params for view state. The URL becomes a shareable description of "what the user is looking at" without leaking state into a global store — a deep link to a filtered, paginated, sorted list comes for free.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
import { useParams } from 'react-router-dom';
function User() { const { id } = useParams(); return <h1>User {id}</h1>; }
Try it Yourself »
Discussion
Loading…