iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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…