# How to Use Router Push Nextjs to Pass State Between Pages

> Learn how to use router push Nextjs to pass state between pages. Serialize data in the URL with query params and easily retrieve it on the next page for seamless navigation.

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

---

**Use the `router.push` method from `next/router` with a URL object containing a `query` property to serialize state into the URL, then deserialize it on the destination page using `useRouter().query`.**

The `router push nextjs` pattern is essential for programmatic navigation with data transfer in React applications. In the [`vercel/next.js`](https://github.com/vercel/next.js/blob/main/vercel/next.js) repository, the client-side router implementation in [`packages/next/src/client/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/router.ts) exposes the `push` method that forwards navigation requests to the internal router reducer. This architecture ensures that state passed via query parameters persists across both client-side transitions and server-side rendering contexts defined in [`packages/next/src/server/lib/router-server.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/lib/router-server.ts).

## Understanding Router Push Nextjs Architecture

The Next.js router implementation spans several critical files that handle state transfer. When you invoke `router.push(url, as?, options?)`, the call flows through [`packages/next/src/client/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/router.ts) into the router reducer at [`packages/next/src/client/components/router-reducer/router-reducer.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts). This reducer updates the shared router context defined in [`packages/next/src/shared/lib/router-context.shared-runtime.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/shared/lib/router-context.shared-runtime.ts), which is consumed by the `useRouter()` hook on both client and server sides.

The core data structures reside 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), ensuring that query parameters remain accessible during server-side rendering (SSR) and static site generation (SSG). This design guarantees that state encoded in the URL survives hard reloads and direct URL access.

## Method 1: Pass State via URL Query Parameters

Encoding state in the URL query string is the most reliable method for `router push nextjs` operations. This approach works consistently across server-rendered pages, client-side navigations, and hard reloads.

### Encoding State on the Origin Page

Serialize complex objects using `JSON.stringify` and pass them through the `query` property of the URL object:

```tsx
// pages/first.tsx
import { useRouter } from 'next/router';
import { useCallback } from 'react';

export default function FirstPage() {
  const router = useRouter();

  const goToSecond = useCallback(() => {
    const data = { name: 'Alice', age: 30 };
    router.push(
      {
        pathname: '/second',
        query: { payload: JSON.stringify(data) },
      },
      undefined,
      { shallow: false }
    );
  }, [router]);

  return (
    <div>
      <h1>First Page</h1>
      <button onClick={goToSecond}>Go to Second Page (with state)</button>
    </div>
  );
}

```

*Source reference:* `router.push` implementation – [`packages/next/src/client/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/router.ts)

### Decoding State on the Destination Page

Access the query parameters using `useRouter` and deserialize the data:

```tsx
// pages/second.tsx
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';

export default function SecondPage() {
  const { query } = useRouter();
  const [payload, setPayload] = useState<{ name: string; age: number } | null>(null);

  useEffect(() => {
    if (query.payload) {
      try {
        setPayload(JSON.parse(query.payload as string));
      } catch {
        // handle malformed data
      }
    }
  }, [query.payload]);

  return (
    <div>
      <h1>Second Page</h1>
      {payload ? (
        <p>
          Received name: <strong>{payload.name}</strong>, age: <strong>{payload.age}</strong>
        </p>
      ) : (
        <p>No data received.</p>
      )}
    </div>
  );
}

```

*Source reference:* `useRouter` reads from the shared router context – [`packages/next/src/shared/lib/router-context.shared-runtime.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/shared/lib/router-context.shared-runtime.ts)

## Method 2: Shallow Routing with Router Push Nextjs

When you need to update the URL and component props without re-fetching data, use the `shallow` option. This modifies the query string without triggering `getServerSideProps`, `getStaticProps`, or `getInitialProps`.

```tsx
router.push(
  { pathname: '/dashboard', query: { tab: 'settings' } },
  undefined,
  { shallow: true }
);

```

*Source reference:* shallow flag handling – [`packages/next/src/client/components/router-reducer/router-reducer.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts)

## Method 3: Masking URLs with the As Parameter

The second argument to `router.push` (the `as` parameter) allows you to mask the actual URL shown in the browser address bar while preserving the underlying query parameters for the page component.

```tsx
router.push(
  { pathname: '/profile/[id]', query: { id: '123', ref: 'email' } },
  '/profile/123',
  { shallow: false }
);

```

In this example, the user sees `/profile/123` in the address bar, but the page receives `query.id` as `'123'` and `query.ref` as `'email'`. The underlying data is stored in the router context defined 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).

*Source reference:* "as" URL handling – [`packages/next/src/client/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/router.ts)

## Summary

- **Query parameters** are the most reliable method for `router push nextjs` state transfer, persisting across hard reloads and SSR/SSG contexts.
- **Serialization** is required for complex objects; use `JSON.stringify` before passing to `query` and `JSON.parse` when reading via `useRouter().query`.
- **Shallow routing** updates the URL without data refetching, controlled by the `shallow: true` option processed in [`packages/next/src/client/components/router-reducer/router-reducer.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts).
- **URL masking** via the `as` parameter hides query parameters from the address bar while maintaining access to them in the router context from [`packages/next/src/shared/lib/router-context.shared-runtime.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/shared/lib/router-context.shared-runtime.ts).

## Frequently Asked Questions

### Can I pass complex objects directly through router push nextjs without serializing?

No, the Next.js router only accepts serializable values in the `query` object. According to the implementation 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), query parameters are encoded into the URL string, which requires primitive values or strings. For complex objects, you must use `JSON.stringify` before passing the data and `JSON.parse` when retrieving it with `useRouter().query`.

### Does shallow routing affect getServerSideProps or getStaticProps?

Yes, shallow routing specifically prevents `getServerSideProps`, `getStaticProps`, and `getInitialProps` from running. When you set `shallow: true` in the options object passed to `router.push`, the router reducer in [`packages/next/src/client/components/router-reducer/router-reducer.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts) updates the URL and component props without triggering data fetching methods, making it ideal for UI state changes that don't require new server data.

### How does the Next.js router handle state during server-side rendering?

During server-side rendering, the router hydrates query parameters from the incoming request URL. The server-side implementation in [`packages/next/src/server/lib/router-server.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/lib/router-server.ts) ensures that query strings are parsed and made available to the router context. When using `router push nextjs` with query parameters, the same data is accessible on both the server (during SSR) and the client (during hydration), ensuring consistency across rendering modes.

### What is the difference between using router.push and the Link component for state transfer?

Both `router.push` and the `Link` component utilize the same underlying router context from [`packages/next/src/shared/lib/router-context.shared-runtime.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/shared/lib/router-context.shared-runtime.ts), but `router.push` is imperative while `Link` is declarative. Use `router.push` when you need to programmatically navigate after business logic (like form submission) or when you need to pass complex serialized state. Use `Link` for static navigation where the destination is known at render time, though you can still pass query parameters via the `href` object in both approaches.