`usePathname` and `useSearchParams` Explained

usePathname reads the current URL path, and useSearchParams reads the query string. Learn how each works and why one needs a Suspense boundary.

7 min read

usePathname in Next.js and its companion hook useSearchParams read two different parts of the current URL from inside a Client Component. The first returns the route itself, and the second returns everything after the question mark.

App.tsxApp.tsx
// app/nav/current-path.tsx
"use client";
import { usePathname } from "next/navigation";
 
export default function CurrentPath() {
  const pathname = usePathname();
  return <p>You are on {pathname}</p>;
}

Visit the blog route with a sort parameter in the query string, and this component still renders "You are on /blog". usePathname strips off the query string and any hash, returning only the path itself, and it updates automatically on every navigation.

Reading the query string

useSearchParams returns a read-only version of the standard URLSearchParams interface, so a value is read with its get method instead of plain object access.

App.tsxApp.tsx
// app/nav/sort-label.tsx
"use client";
import { useSearchParams } from "next/navigation";
 
export default function SortLabel() {
  const params = useSearchParams();
  const sort = params.get("sort") ?? "default";
  return <p>Sorted by {sort}</p>;
}

Add a sort parameter set to "new" to the URL and this renders "Sorted by new". Remove that parameter entirely, and the fallback text appears instead, because the get method returns nothing for a key that is not present in the current query string.

HookReturns
usePathnameThe route path, no query string or hash
useSearchParamsA read-only view of the query string

There is no method on this object for setting a new value, since it only reads the URL. To change a query parameter, build a new query string from the current one and navigate to it with the useRouter hook.

The Suspense requirement

Calling the search params hook in a component that Next.js tries to prerender makes that component, and everything above it up to the nearest Suspense boundary, render on the client instead. Wrapping the component that reads it in Suspense keeps everything else prerendered normally.

App.tsxApp.tsx
// app/blog/page.tsx
import { Suspense } from "react";
import SortLabel from "@/app/nav/sort-label";
 
export default function BlogPage() {
  return (
    <Suspense fallback={<p>Loading sort order…</p>}>
      <SortLabel />
    </Suspense>
  );
}

This shows the fallback text briefly on a static build, then the real sort label once the client finishes rendering it. Skipping the boundary still works in local development, which is exactly what makes this mistake easy to miss, but a production build of a static route fails outright without it.

Neither hook works on the server

A Server Component cannot call either hook directly, since both depend on browser APIs that only exist once the component has hydrated on the client. A page reads the same information through its own params and searchParams props instead, which arrive as plain values rather than a hook result, covered in Next.js params vs searchParams: How to Read URL Parameters. For building an active navigation link with segment data instead of a raw path string, see useSelectedLayoutSegment and Building Active Nav Links.

Rune AI

Rune AI

Key Insights

  • usePathname returns the path only, without the query string or hash.
  • useSearchParams returns a read-only URLSearchParams instance, not a plain object.
  • Neither hook works in a Server Component, both require a client directive.
  • Wrap a component that calls useSearchParams in Suspense before a production build.
  • To update the query string, build a new URLSearchParams value and navigate with the router.
RunePowered by Rune AI

Frequently Asked Questions

Do these hooks work in a Server Component?

No. Both are Client Component hooks. A Server Component reads the same information through the page's params and searchParams props instead.

Can I set a new value directly on the object useSearchParams returns?

No. It returns a read-only URLSearchParams instance. Build a new query string and navigate to it with the router instead.

Why does my build fail after adding useSearchParams to a static page?

A Client Component that calls useSearchParams needs a Suspense boundary around it during a production build, or the build fails instead of silently falling back like it does in development.

Conclusion

usePathname returns the current path as a plain string, and useSearchParams returns a read-only view of the query string. Both only work in a Client Component, and a component that calls useSearchParams during static rendering needs a Suspense boundary so the rest of the page can still prerender.