next/router

先に進む前に、まずはルーティング入門をお読みになることをお勧めします。

useRouter

アプリ内の関数コンポーネントの中で router オブジェクト へアクセスしたいときは、 useRouter フックを利用できます。以下の例をご覧ください:

import { useRouter } from 'next/router';

function ActiveLink({ children, href }) {
  const router = useRouter();
  const style = {
    marginRight: 10,
    color: router.pathname === href ? 'red' : 'black'
  };

  const handleClick = e => {
    e.preventDefault();
    router.push(href);
  };

  return (
    <a href={href} onClick={handleClick} style={style}>
      {children}
    </a>
  );
}

export default ActiveLink;

useRouter は、React Hook であり、したがってクラスとともに使用することはできません。withRouter を利用するか、関数コンポーネントでクラスをラップしてください。

router オブジェクト

useRouterwithRouter の両方から返却される router オブジェクトの定義は以下のとおりです:

  • pathname: String - 現在のルートです。 これは /pages ディレクトリでのページのパスです。設定された basePathlocale は含まれません。
  • query: Object - オブジェクトに解釈されたクエリ文字列です。ページにデータ取得要求 が含まれなければ、プリレンダリング時には空オブジェクトになります。デフォルトでは {} になります。
  • asPath: String - ブラウザに表示される実際のパス(クエリも含まれる)です。設定された basePathlocale は含まれません。
  • isFallback: boolean - Whether the current page is in fallback mode.
  • basePath: String - The active basePath (if enabled).
  • locale: String - The active locale (if enabled).
  • locales: String[] - All supported locales (if enabled).
  • defaultLocale: String - The current default locale (if enabled).
  • domainLocales: Array<{domain, defaultLocale, locales}> - Any configured domain locales.
  • isReady: boolean - Whether the router fields are updated client-side and ready for use. Should only be used inside of useEffect methods and not for conditionally rendering on the server. See related docs for use case with automatically statically optimized pages
  • isPreview: boolean - Whether the application is currently in preview mode.

The following methods are included inside router:

router.push

クライアント側のページ遷移を処理します。このメソッドは next/link では不十分な場合に役立ちます。

router.push(url, as, options);
  • url:UrlObject | String - 遷移先の URL です。 (see Node.JS URL module documentation for UrlObject properties)
  • as: UrlObject | String - URL のオプションのデコレータで、ブラウザに表示されます。Before Next.js 9.5.3 this was used for dynamic routes, check our previous docs to see how it worked. Note: when this path differs from the one provided in href the previous href/as behavior is used as shown in the previous docs
  • options - 以下の設定オプションを持つ、オプションのオブジェクトです:
    • scroll - Optional boolean, controls scrolling to the top of the page after navigation. Defaults to true
    • shallow: getStaticPropsgetServerSideProps あるいは getInitialProps を再実行することなく現在のページのパスを更新します。デフォルトでは false です。
    • locale - Optional string, indicates locale of the new page

外部 URL に対しては router.push を使う必要がありません。この場合は window.location がより適しています。

使い方

事前に定義されたルートである pages/about.js へ遷移する場合:

import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.push('/about')}>
      Click me
    </button>
  )
}

動的ルートの pages/post/[pid].js へ遷移する場合:

import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.push('/post/abc')}>
      Click me
    </button>
  )
}

Note: When navigating to the same page in Next.js, the page's state will not be reset by default, as the top-level React component is the same. You can manually ensure the state is updated using useEffect.

Redirecting the user to pages/login.js, useful for pages behind 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)) {
      router.push('/login')
    }
  }, [user, loading])

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

URL オブジェクトと一緒に利用する

URL オブジェクトは、next/link で URL オブジェクトを用いる場合と同じように扱うことができます。urlas パラメータの両方で有効です:

import { useRouter } from 'next/router'

export default function ReadMore({ post }) {
  const router = useRouter()

  return (
    <button
      type="button"
      onClick={() => {
        router.push({
          pathname: '/post/[pid]',
          query: { pid: post.id },
        })
      }}
    >
      Click here to read more
    </button>
  )
}

router.replace

next/linkreplace prop に似ています。router.replace を使うと、history スタックに新しい URL エントリは追加されません。次の例をご覧ください:

router.replace(url, as, options)

Router.replace の API は Router.push で使われる API と全く同じです。

Usage

Take a look at the following example:

import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.replace('/home')}>
      Click me
    </button>
  )
}

router.prefetch

クライアント側のページ遷移を高速化するためにページをプリフェッチします。このメソッドは next/link を用いないページ遷移においてのみ有用です。というのも、next/link は自動的にページのプリフェッチ周りの面倒を見てくれるからです。

この API は、本番環境限定の機能です。Next.js は開発環境ではページをプリフェッチしません。

router.prefetch(url, as)
  • url - The URL to prefetch, including explicit routes (e.g. /dashboard) and dynamic routes (e.g. /product/[id])
  • as - url のオプションのデコレータであり、Before Next.js 9.5.3 this was used to prefetch dynamic routes, check our previous docs to see how it worked

使い方

例えば、ログインページがあり、ログイン後にユーザーをダッシュボードへリダイレクトさせるとしましょう。こうしたケースでは、より高速にページ遷移すべくダッシュボードをプリフェッチできます。次の例のようになります:

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

export default function Login() {
  const router = useRouter()
  const handleSubmit = 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')
    })
  }, [])

  useEffect(() => {
    // Prefetch the dashboard page
    router.prefetch('/dashboard')
  }, [])

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

router.beforePopState

場合によっては(例えば、カスタムサーバーを使用する場合)、popstate をリッスンして、ルーターが動作する前に何かしたいということがあります。

router.beforePopState(cb)
  • cb - 入力された popstate イベントに対して実行される関数です。この関数は、以下の props を持つオブジェクトとしてイベントの状態を受け取ります:
    • url: String - 新しい state のためのルートです。これは通常 page の名前です。
    • as: String - ブラウザに表示される URL です。
    • options: Object - Router.push によって送信される追加のオプションです。

beforePopState に渡した関数が false を返却する場合、Routerpopstate を処理しないため、その場合は自分の責任で popstate を処理することになります。 ファイルシステムのルーティングを無効化する をご覧ください。

Usage

これを用いてリクエストを操作したり、SSR によるリフレッシュを強制したりできます。以下に示す例のようになります:

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

export default function Page() {
  const router = useRouter()

  useEffect(() => {
    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
    })
  }, [])

  return <p>Welcome to the page</p>
}

router.back

履歴を遡ります。ブラウザの戻るボタンをクリックするのと同じです。window.history.back() が実行されます。

Usage

import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.back()}>
      Click here to go back
    </button>
  )
}

router.reload

現在の URL をリロードします。ブラウザの更新ボタンをクリックするのと同じです。window.location.reload() が実行されます。

Usage

import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.reload()}>
      Click here to reload
    </button>
  )
}

Router.events

Router 内で発生する様々なイベントをリッスンできます。こちらがサポートされているイベントの一覧です:

  • routeChangeStart(url, { shallow }) - ルートの変更が開始した時に発火します。
  • routeChangeComplete(url, { shallow }) - ルートが完全に変更され終わったときに発火します。
  • routeChangeError(err, url, { shallow }) - ルート変更時にエラーが発生した際、あるいはルートの読み込みが中止された際に発火します。
    • err.cancelled - ページ遷移が中止されたかどうかを示します。
  • beforeHistoryChange(url, { shallow }) -ブラウザの履歴を変更する直前に発火します。
  • hashChangeStart(url, { shallow }) - ハッシュが変更されるが、ページが変更されないときに発火します。
  • hashChangeComplete(url, { shallow }) - ハッシュの変更が完了したが、ページが変更されないときに実行されます。

ここでの urlbasePath を含むブラウザに表示されている URL です。

使い方

例えば、ルーターのイベント routeChangeStart をリッスンするには, open or create pages/_app.js and subscribe to the event, like so:

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

export default function MyApp({ Component, pageProps }) {
  const router = useRouter()

  useEffect(() => {
    const handleRouteChange = (url, { shallow }) => {
      console.log(
        `App is changing to ${url} ${
          shallow ? 'with' : 'without'
        } shallow routing`
      )
    }

    router.events.on('routeChangeStart', handleRouteChange)

    // If the component is unmounted, unsubscribe
    // from the event with the `off` method:
    return () => {
      router.events.off('routeChangeStart', handleRouteChange)
    }
  }, [])

  return <Component {...pageProps} />
}

We use a Custom App (pages/_app.js) for this example to subscribe to the event because it's not unmounted on page navigations, but you can subscribe to router events on any component in your application.

ルーターのイベントは、コンポーネントがマウントされたとき(useEffect または componentDidMount / componentWillUnmount)あるいはイベント発生時に、imperatively に登録する必要があります

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 'next/router';

Router.events.off('routeChangeStart', handleRouteChange);

ルートの読み込みがキャンセルされたら(例えば、2 つのリンクを続けて素早くクリックした場合)、routeChangeError が発火します。そして、渡される err には、以下の例のように、true がセットされた cancelled プロパティが含まれます:

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

export default function MyApp({ Component, pageProps }) {
  const router = useRouter()

  useEffect(() => {
    const handleRouteChangeError = (err, url) => {
      if (err.cancelled) {
        console.log(`Route to ${url} was cancelled!`)
      }
    }

    router.events.on('routeChangeError', handleRouteChangeError)

    // If the component is unmounted, unsubscribe
    // from the event with the `off` method:
    return () => {
      router.events.off('routeChangeError', handleRouteChangeError)
    }
  }, [])

  return <Component {...pageProps} />
}

Potential ESLint errors

Certain methods accessible on the router object return a Promise. If you have the ESLint rule, no-floating-promises enabled, consider disabling it either globally, or for the affected line.

If your application needs this rule, you should either void the promise – or use an async function, await the Promise, then void the function call. This is not applicable when the method is called from inside an onClick handler.

The affected methods are:

  • router.push
  • router.replace
  • router.prefetch

Potential solutions

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(() => {
    // disable the linting on the next line - This is the cleanest solution
    // eslint-disable-next-line no-floating-promises
    router.push('/login')

    // void the Promise returned by router.push
    if (!(user || loading)) {
      void router.push('/login')
    }
    // or use an async function, await the Promise, then void the function call
    async function handleRouteChange() {
      if (!(user || loading)) {
        await router.push('/login')
      }
    }
    void handleRouteChange()
  }, [user, loading])

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

withRouter

useRouter が最適でない場合、withRouter を使うことで同様の router オブジェクトをコンポーネントに加えることができます。使い方は以下のとおりです:

import { withRouter } from 'next/router';

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

export default withRouter(Page);

TypeScript

To use class components with withRouter, the component needs to accept a router prop:

import React from 'react'
import { withRouter, NextRouter } from 'next/router'

interface WithRouterProps {
  router: NextRouter
}

interface MyComponentProps extends WithRouterProps {}

class MyComponent extends React.Component<MyComponentProps> {
  render() {
    return <p>{this.props.router.pathname}</p>
  }
}

export default withRouter(MyComponent)