# How to Get Query Params in Next.js: The Most Efficient Method

> Learn the most efficient method to nextjs get query params using the useRouter hook. Access URL parameters instantly with a cached router query object for optimal performance.

- Repository: [Vercel/next.js](https://github.com/vercel/next.js)
- Tags: how-to-guide
- Published: 2026-02-16

---

**The most efficient way to get query params in Next.js is using the `useRouter` hook from `next/router`, which provides a cached `router.query` object containing all URL parameters without re-parsing on every render.**

When building applications with Next.js, accessing URL query parameters is a common requirement for features like search filters, pagination, and referral tracking. The framework provides a built-in solution that eliminates manual URL parsing and ensures optimal performance through internal caching mechanisms.

## Why useRouter is the Optimal Solution for Next.js Query Params

Next.js implements a **singleton router pattern** that parses the URL once during navigation and stores the resulting query parameters in an internal state object. This architecture makes `router.query` access extremely fast because it returns a cached plain object rather than parsing `window.location.search` on every component render.

According to the Next.js source code in [`packages/next/src/client/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/router.ts), the router singleton forwards property accesses including `query` via defined getters to the underlying Router instance. The core Router class in [`packages/next/src/shared/lib/router/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/shared/lib/router/router.ts) maintains the `query` object as part of its immutable state, updating it only when navigation occurs through the `change` method.

## How to Get Query Params in Next.js: Three Implementation Methods

### Method 1: useRouter Hook in Functional Components

The `useRouter` hook is the standard approach for functional components. It reads the router instance from React context and returns the cached `query` object immediately.

```typescript
import { useRouter } from 'next/router'

export default function SearchPage() {
  const { query } = useRouter()
  
  // query is typed as ParsedUrlQuery { [key: string]: string | string[] | undefined }
  const searchTerm = query.term as string
  const pageNumber = query.page || '1'
  
  return (
    <div>
      <h1>Results for: {searchTerm}</h1>
      <p>Page: {pageNumber}</p>
    </div>
  )
}

```

The hook implementation in [`packages/next/src/client/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/router.ts) (lines 138-146) simply accesses `RouterContext` and returns the current router value, ensuring components re-render automatically when the router state changes.

### Method 2: withRouter HOC in Class Components

For class-based components, Next.js provides the `withRouter` higher-order component that injects the router as a prop.

```typescript
import { withRouter, NextRouter } from 'next/router'
import { Component } from 'react'

interface Props {
  router: NextRouter
}

class ProductDetail extends Component<Props> {
  render() {
    const { id, category } = this.props.router.query
    
    return (
      <div>
        <h1>Product {id}</h1>
        <p>Category: {category}</p>
      </div>
    )
  }
}

export default withRouter(ProductDetail)

```

The `withRouter` implementation in [`packages/next/src/client/with-router.tsx`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/with-router.tsx) wraps the component and passes the singleton router from context as a prop, providing the same `query` object available via the hook.

### Method 3: Server-Side Access in getServerSideProps

When rendering on the server, query parameters are available through the context object passed to data fetching functions.

```typescript
import { GetServerSideProps } from 'next'

export const getServerSideProps: GetServerSideProps = async (context) => {
  const { term, page = '1' } = context.query
  
  // Fetch data based on query params
  const data = await fetchSearchResults(term as string, parseInt(page as string))
  
  return {
    props: {
      results: data,
      searchTerm: term || ''
    }
  }
}

```

While `getServerSideProps` runs on the server for the initial render, the client-side component should still use `useRouter` for subsequent client-side navigations to avoid unnecessary server round-trips.

## Architecture Deep Dive: How Next.js Caches Query Parameters

Understanding why `useRouter` is efficient requires examining the internal architecture. The Next.js router implements several optimization strategies:

**Singleton Pattern with Lazy Initialization**

The client-side router is created as a singleton in [`packages/next/src/client/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/router.ts). The `singletonRouter` object uses property descriptors to forward accesses to the actual Router instance only when needed, preventing unnecessary initialization during module load.

**Immutable State Management**

The core Router class in [`packages/next/src/shared/lib/router/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/shared/lib/router/router.ts) maintains an immutable state object containing the `query` field. When navigation occurs via the `change` method, the URL is parsed once using the internal `parseRelativeUrl` utility, and the resulting query object is stored in state. Subsequent reads of `router.query` return this cached object reference.

**React Context Integration**

The `useRouter` hook (lines 138-146 in [`packages/next/src/client/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/router.ts)) retrieves the router from `RouterContext`. Because the router reference is stable and the query object is part of the router's state, React's change detection efficiently triggers re-renders only when the query actually changes, not on every render cycle.

This architecture ensures that accessing query parameters via `useRouter` has **O(1)** complexity with no string parsing overhead, making it significantly more efficient than manually parsing `window.location.search` with `URLSearchParams` on every render.

## Summary

- **Use `useRouter`** from `next/router` to access query params in functional components—it's the most efficient method due to internal caching.
- **Access `router.query`** to get a plain object containing all URL parameters; values may be strings or arrays for repeated keys.
- **Use `withRouter`** for class components to inject the same router instance as a prop.
- **Server-side fetching** via `getServerSideProps` receives `context.query` with the same shape for initial renders.
- **Avoid manual parsing** of `window.location`—the Next.js router parses the URL once during navigation and caches the result in [`packages/next/src/shared/lib/router/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/shared/lib/router/router.ts).

## Frequently Asked Questions

### How do I handle array values in Next.js query params?

When a URL contains repeated query keys (e.g., `?tag=react&tag=nextjs`), Next.js automatically parses them as an array in `router.query` (e.g., `{ tag: ['react', 'nextjs'] }`). Always check if a value is an array using `Array.isArray()` before processing, or normalize it with `[].concat(value)` to handle both single and multiple values consistently.

### What is the difference between using useRouter in the Pages Router versus the App Router?

In the **Pages Router** (`pages/` directory), you import `useRouter` from `next/router` and access `router.query`. In the **App Router** (`app/` directory), you use `useSearchParams` from `next/navigation` instead, which returns a `URLSearchParams` object rather than a plain object. The Pages Router's `useRouter` caches the parsed query object, while the App Router's `useSearchParams` reads from the current URL search params directly.

### Can I use useRouter in getServerSideProps or getStaticProps?

No, `useRouter` is a **client-side only** hook that relies on React context. In `getServerSideProps` or `getStaticProps`, access query parameters through the `context` argument's `query` property instead: `context.query`. This provides the same shape of data (an object with string or array values) but works during server-side rendering before the React hydration occurs.

### Is router.query available during the initial render of a page?

In the **Pages Router**, `router.query` may be **empty** during the initial server-side render or the first client-side render for statically optimized pages, because Next.js needs to parse the URL first. For server-rendered pages using `getServerSideProps`, the query is populated immediately. For client-side transitions, `router.query` updates instantly. To handle the initial empty state, use conditional rendering or check if `router.isReady` is true before accessing query values.