Handling custom page navigation on Next.js

Last month we discussed about different kind of routing on Next.js. While next/link is enough for all of our routing requirements, we can also handle client side navigation without using it.

Here's a short example of creating a component that moves the user to next page. You need to import the useRouter component from next/router, then you can just router.push(url)

import { useRouter } from 'next/router'

function NextPage() {
  const router = useRouter()

  return (
    <span onClick={() => router.push('/some_different_page')}>
      Visit next page 

export default NextPage

The useRouter is a react hook, so by default it will only work with functional react components, and not work with class components. If you want similar behavior on class components, you need to use withRouter. Both useRouter and withRouter will return the router object, so everything else is similar.

you can read more about this here.

import { withRouter } from 'next/router'

function Page({ router }) {
  return <span>{router.pathname}</span>

export default withRouter(Page)


This is the core function for handling custom navigation of your next.js app. You use it like

router.push("some url")

You can also send some optional params

router.push("some url", as, options)

optional decorator for the URL that users will see. Prior to next v9.5 this was used for handling dynamic routes.


optional object you send as param. Inside the object, it takes shallow, getStaticProps, getInitialProps, getServerSideProps

Here's an example

router.push('/?counter=10', undefined, { shallow: true })

By default, the options you don't mention inside the object will be false, so in the above example getStaticProps, getInitialProps, getServerSideProps all will be set as false.

A good example is, redirecting the user to login page, after he requests for something that requires authentication.

import { useEffect } from 'react'
import { useRouter } from 'next/router'

// Here you would fetch and return the user
const useUser = () => ({ user: null, loading: false })

export default function Page() {
  const { user, loading } = useUser()
  const router = useRouter()

  useEffect(() => {
    if (!(user || loading)) {
  }, [user, loading])

  return <p>Redirecting...</p>

One thing to note here, you don't need to use router.push when navigating to an external link. In those cases, just using the browser's window.location is enough.


this is useful for when you don't want any entry on your history. Other than that, it's exactly similar to router.push

router.replace("some url you don't want in history", as, options)


Reloads the current page, duh. It executes



Moves the user one page ago in history. It executes



Use prefetch to make your pages appear faster on production. (automatically handled when using next/link) You use it like

router.prefetch("some url", as) // as is optional like in router.push

Here's an excellent scenario from next.js doc.

Imagine a case where the user will see the dashbord after he logs in successfully. In this case, we can prefetch the dashboard, while the user is still in login page, thus make the experience snappier.

import { useCallback, useEffect } from 'react'
import { useRouter } from 'next/router'

export default function Login() {
  const router = useRouter()
  const handleSubmit = useCallback((e) => {

    fetch('/api/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        /* Form data */
    }).then((res) => {
      // Do a fast client-side transition to the already prefetched dashboard page
      if (res.ok) router.push('/dashboard')
  }, [])

  useEffect(() => {
    // Prefetch the dashboard page
  }, [])

  return (
    <form onSubmit={handleSubmit}>
      {/* Form fields */}
      <button type="submit">Login</button>

Learning Material

Obviously, the next.js/doc

All Rights Reserved

Let's register a Viblo Account to get more interesting posts.