Blitz is now in beta! 🎉 1.0 expected this April
Back to Documentation Menu

Router

Topics

Jump to a Topic

There are two ways to access router within components.

  1. useRouter() (recommended)
  2. withRouter()

useRouter

import {useRouter} from "blitz"

function Thing({href}) {
  const router = useRouter()

  return (
    <div
      style={{
        color: router.pathname === href ? "red" : "black",
      }}
    />
  )
}

export default Thing

useRouter is a React Hook, meaning it cannot be used with classes. You can either use withRouter or wrap your class in a function component.

withRouter

import {withRouter} from "blitz"

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

export default withRouter(Page)

router object

Here's the definition of the router object returned by useRouter and withRouter:

  • pathname: String - Current route. That is the path of the page in /pages
  • query: Object - All the query string parameters from the current url. Parameter type is always string.
  • params: Object - All the dynamic route parameters for the current route. Parameter types are string or string[].
  • asPath: String - Actual path (including the query) shown in the browser
  • push(): Make page navigation
  • replace(): Make page navigation without adding to browser history
  • back(): Navigate to previous history location
  • reload(): Reload the page
  • prefetch(): Prefetch pages for faster client-side transitions
  • events: Subscribe to various router events
  • beforePopState(): For advanced routing needs

Router API

The following router APIs can also be used via import {Router} from 'blitz'

Router.push

Handles client-side transitions, this method is useful for cases where <Link> is not enough.

import {Router} from "blitz"

Router.push(url, as, options)
  • url - The URL to navigate to. This is usually the name of a page
  • as - Optional decorator for the URL that will be shown in the browser. Defaults to url
  • options - Optional object with the following configuration options:

You don't need to use Router for external URLs, window.location is better suited for those cases.

Usage

Navigating to pages/about.js, which is a predefined route:

import {Router} from "blitz"

function Page() {
  return <span onClick={() => Router.push("/about")}>Click me</span>
}

Navigating pages/post/[pid].js, which is a dynamic route:

import {Router} from "blitz"

function Page() {
  return (
    <span onClick={() => Router.push("/post/[pid]", "/post/abc")}>
      Click me
    </span>
  )
}

With URL object

You can use an URL object in the same way you can use it for <Link>. Works for both the url and as parameters:

import {Router} from "blitz"

const handler = () => {
  Router.push({
    pathname: "/about",
    query: {name: "Vercel"},
  })
}

function ReadMore() {
  return (
    <div>
      Click <span onClick={handler}>here</span> to read more
    </div>
  )
}

export default ReadMore

Router.replace

Similar to the replace prop in <Link>, Router.replace will prevent adding a new URL entry into the history stack, take a look at the following example:

import {Router} from "blitz"

Router.replace("/home")

The API for Router.replace is exactly the same as that used for Router.push.

Router.back

Navigate back in history. Equivalent to clicking the browser’s back button. It executes window.history.back().

import {Router} from "blitz"

Router.back()

Router.reload

Reload the current URL. Equivalent to clicking the browser’s refresh button. It executes window.location.reload().

import {Router} from "blitz"

Router.reload()

Router.prefetch

Prefetch pages for faster client-side transitions. This method is only useful for navigations without <Link> because it takes care of prefetching pages automatically.

This is a production only feature. Next.js doesn't prefetch pages on development.

import {Router} from "blitz"

Router.prefetch(url, as)
  • url - The path to a page inside the pages directory
  • as - Optional decorator for url, used to prefetch dynamic routes. Defaults to url

Usage

Let's say you have a login page, and after a login, you redirect the user to the dashboard. For that case, we can prefetch the dashboard to make a faster transition, like in the following example:

import {Router} from "blitz"

export default function Login() {
  const handleSubmit = React.useCallback((e) => {
    e.preventDefault()

    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")
    })
  }, [])

  React.useEffect(() => {
    // Prefetch the dashboard page as the user will go there after the login
    Router.prefetch("/dashboard")
  }, [])

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

Router.events

You can listen to different events happening inside the Router. Here's a list of supported events:

  • routeChangeStart(url) - Fires when a route starts to change
  • routeChangeComplete(url) - Fires when a route changed completely
  • routeChangeError(err, url) - Fires when there's an error when changing routes, or a route load is cancelled
    • err.cancelled - Indicates if the navigation was cancelled
  • beforeHistoryChange(url) - Fires right before changing the browser's history
  • hashChangeStart(url) - Fires when the hash will change but not the page
  • hashChangeComplete(url) - Fires when the hash has changed but not the page

Here url is the URL shown in the browser. If you call Router.push(url, as) (or similar), then the value of url will be as.

For example, to listen to the router event routeChangeStart, do the following:

import {Router} from "blitz"

const handleRouteChange = (url) => {
  console.log("App is changing to: ", url)
}

Router.events.on("routeChangeStart", handleRouteChange)

If you no longer want to listen to the event, unsubscribe with the off method:

import {Router} from "blitz"

Router.events.off("routeChangeStart", handleRouteChange)

If a route load is cancelled (for example, by clicking two links rapidly in succession), routeChangeError will fire. And the passed err will contain a cancelled property set to true, as in the following example:

import {Router} from "blitz"

Router.events.on("routeChangeError", (err, url) => {
  if (err.cancelled) {
    console.log(`Route to ${url} was cancelled!`)
  }
})

Router events should be registered when a component mounts (useEffect or componentDidMount / componentWillUnmount) or imperatively when an event happens, as in the following example:

import {Router} from "blitz"

useEffect(() => {
  const handleRouteChange = (url) => {
    console.log("App is changing to: ", url)
  }

  Router.events.on("routeChangeStart", handleRouteChange)
  return () => {
    Router.events.off("routeChangeStart", handleRouteChange)
  }
}, [])

Router.beforePopState

In some cases (for example, if using a Custom Server), you may wish to listen to popstate and do something before the router acts on it.

You could use this to manipulate the request, or force a SSR refresh, as in the following example:

import {Router} from "blitz"

Router.beforePopState(({url, as, options}) => {
  // I only want to allow these two routes!
  if (as !== "/" && as !== "/other") {
    // Have SSR render bad routes as a 404.
    window.location.href = as
    return false
  }

  return true
})

Router.beforePopState(cb: () => boolean)

  • cb - The function to run on incoming popstate events. The function receives the state of the event as an object with the following props:
    • url: String - the route for the new state. This is usually the name of a page
    • as: String - the url that will be shown in the browser
    • options: Object - Additional options sent by Router.push

If the function you pass into beforePopState returns false, Router will not handle popstate and you'll be responsible for handling it, in that case. See Disabling file-system routing.


Idea for improving this page? Edit it on Github.