Menu

Link

<Link> は、HTML の <a> 要素を拡張したReactコンポーネントで、ルート間のプリフェッチとクライアントサイドナビゲーションを提供します。Next.jsでルート間を移動する主要な方法です。

基本的な使い方:

pages/index.tsx
TypeScript
import Link from 'next/link'
 
export default function Home() {
  return <Link href="/dashboard">Dashboard</Link>
}

リファレンス

<Link> コンポーネントには以下のpropsを渡すことができます:

Prop必須
hrefhref="/dashboard"String または Objectはい
replacereplace={false}Boolean-
scrollscroll={false}Boolean-
prefetchprefetch={false}Boolean-
legacyBehaviorlegacyBehavior={true}Boolean-
passHrefpassHref={true}Boolean-
shallowshallow={false}Boolean-
localelocale="fr"String または Boolean-
onNavigateonNavigate={(e) => {}}Function-

補足: classNametarget="_blank" などの <a> タグの属性は、propsとして <Link> に追加でき、基礎となる <a> 要素に渡されます。

href (必須)

遷移先のパスまたはURL。

pages/index.tsx
TypeScript
import Link from 'next/link'
 
// /about?name=test に遷移
export default function Home() {
  return (
    <Link
      href={{
        pathname: '/about',
        query: { name: 'test' },
      }}
    >
      About
    </Link>
  )
}

replace

デフォルトは false true の場合、next/link は新しいURLをブラウザの履歴スタックに追加する代わりに、現在の履歴状態を置き換えます。

pages/index.tsx
TypeScript
import Link from 'next/link'
 
export default function Home() {
  return (
    <Link href="/dashboard" replace>
      Dashboard
    </Link>
  )
}

scroll

デフォルトは true Next.jsにおける <Link> のデフォルトのスクロール動作は、スクロール位置を維持することであり、ブラウザの戻る/進むナビゲーションと同様の動作です。新しいPageに移動する場合、そのPageがビューポート内に表示されている限り、スクロール位置は同じままです。ただし、Pageがビューポート内に表示されていない場合、Next.jsは最初のPage要素の先頭にスクロールします。

scroll = {false} の場合、Next.jsは最初のPage要素へのスクロールを試みません。

補足: Next.jsはスクロール動作を管理する前に scroll: false をチェックします。スクロールが有効な場合、関連するDOMノードを特定し、各トップレベル要素を検査します。スクロール不可能な要素や、レンダリングされたHTMLを持たない要素(stickyまたはfixed位置の要素、viewport内に表示されていない要素など)はスキップされます。Next.jsは、ビューポート内に表示されるスクロール可能な要素が見つかるまで、兄弟要素を探し続けます。

pages/index.tsx
TypeScript
import Link from 'next/link'
 
export default function Home() {
  return (
    <Link href="/dashboard" scroll={false}>
      Dashboard
    </Link>
  )
}

prefetch

プリフェッチは、<Link /> コンポーネントがユーザーのビューポートに入ったとき(初期表示時またはスクロール時)に発生します。Next.jsはリンク先のルート(href で指定されたもの)とデータをバックグラウンドでプリフェッチし、クライアントサイドナビゲーションのパフォーマンスを向上させます。プリフェッチは本番環境でのみ有効です

prefetch プロップには以下の値を渡すことができます:

  • true(デフォルト): ルート全体とそのデータがプリフェッチされます。
  • false: ビューポートに入ったときにはプリフェッチは行われませんが、ホバー時には行われます。ホバー時のフェッチも完全に無効にしたい場合は、<a> タグを使用するか、App Routerを段階的に採用することを検討してください。これにより、ホバー時のプリフェッチを無効にすることもできます。
pages/index.tsx
TypeScript
import Link from 'next/link'
 
export default function Home() {
  return (
    <Link href="/dashboard" prefetch={false}>
      Dashboard
    </Link>
  )
}

legacyBehavior

<Link> の子要素として <a> 要素は不要になりました。legacyBehavior プロップを追加して従来の動作を使用するか、<a> を削除してアップグレードしてください。コードを自動的にアップグレードするためのcodemodが利用可能です。

補足: legacyBehaviortrue に設定されていない場合、classNameonClick などの anchor タグのすべてのプロパティは next/link にも渡すことができます。

passHref

Linkhref プロパティを子要素に強制的に送信させます。デフォルトは false です。詳細については、関数コンポーネントを渡すの例を参照してください。

shallow

現在のページのパスを更新しますが、getStaticPropsgetServerSidePropsgetInitialProps を再実行しません。デフォルトは false です。

pages/index.tsx
TypeScript
import Link from 'next/link'
 
export default function Home() {
  return (
    <Link href="/dashboard" shallow={false}>
      Dashboard
    </Link>
  )
}

locale

アクティブなロケールは自動的に先頭に追加されます。locale を指定すると、異なるロケールを提供できます。false の場合、デフォルトの動作が無効になるため、href にロケールを含める必要があります。

pages/index.tsx
TypeScript
import Link from 'next/link'
 
export default function Home() {
  return (
    <>
      {/* デフォルトの動作: ロケールが先頭に追加される */}
      <Link href="/dashboard">Dashboard (with locale)</Link>
 
      {/* ロケールの先頭追加を無効化 */}
      <Link href="/dashboard" locale={false}>
        Dashboard (without locale)
      </Link>
 
      {/* 異なるロケールを指定 */}
      <Link href="/dashboard" locale="fr">
        Dashboard (French)
      </Link>
    </>
  )
}

onNavigate

クライアントサイドナビゲーション中に呼び出されるイベントハンドラです。このハンドラは preventDefault() メソッドを含むイベントオブジェクトを受け取り、必要に応じてナビゲーションをキャンセルすることができます。

app/page.tsx
TypeScript
import Link from 'next/link'
 
export default function Page() {
  return (
    <Link
      href="/dashboard"
      onNavigate={(e) => {
        // SPAナビゲーション中にのみ実行されます
        console.log('Navigating...')
 
        // 必要に応じてナビゲーションを阻止できます
        // e.preventDefault()
      }}
    >
      Dashboard
    </Link>
  )
}

補足: onClickonNavigate は似ているように見えますが、異なる目的で使用されます。onClick はすべてのクリックイベントで実行されますが、onNavigate はクライアントサイドナビゲーション中にのみ実行されます。主な違いは以下の通りです:

  • 修飾キー(Ctrl/Cmd + クリック)を使用する場合、onClick は実行されますが、Next.jsが新しいタブのデフォルトナビゲーションを防止するため、onNavigate は実行されません。
  • 外部URLは onNavigate をトリガーしません。これはクライアントサイドおよび同一オリジンのナビゲーションに限定されるためです。
  • download 属性を持つリンクは、ブラウザがリンク先URLをダウンロードとして扱うため、onClick では動作しますが、onNavigate では動作しません。

以下の例は、さまざまなシナリオで <Link> コンポーネントを使用する方法を示しています。

動的ルートセグメントへのリンク

動的ルートセグメントでは、テンプレートリテラルを使用してリンクのパスを作成すると便利です。

例えば、動的ルート pages/blog/[slug].js へのリンクのリストを生成できます:

pages/blog/index.tsx
TypeScript
import Link from 'next/link'
 
function Posts({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <Link href={`/blog/${post.slug}`}>{post.title}</Link>
        </li>
      ))}
    </ul>
  )
}

子要素が <a> タグをラップするカスタムコンポーネントの場合

Link の子要素が <a> タグをラップするカスタムコンポーネントの場合、LinkpassHref を追加する必要があります。これは、styled-componentsなどのライブラリを使用している場合に必要です。これがないと、<a> タグに href 属性がなくなり、サイトのアクセシビリティが損なわれ、SEOに影響する可能性があります。ESLintを使用している場合、passHref の正しい使用を確認するための組み込みルール next/link-passhref があります。

components/nav-link.tsx
TypeScript
import Link from 'next/link'
import styled from 'styled-components'
 
// <a>タグをラップするカスタムコンポーネントを作成
const RedLink = styled.a`
  color: red;
`
 
function NavLink({ href, name }) {
  return (
    <Link href={href} passHref legacyBehavior>
      <RedLink>{name}</RedLink>
    </Link>
  )
}
 
export default NavLink
  • emotionのJSX pragma機能(@jsx jsx)を使用している場合、直接 <a> タグを使用しても passHref を使用する必要があります。
  • コンポーネントは、ナビゲーションを正しくトリガーするために onClick プロパティをサポートする必要があります。

関数コンポーネントをネストする

Link の子要素が関数コンポーネントの場合、passHreflegacyBehavior を使用することに加えて、コンポーネントを React.forwardRef でラップする必要があります:

pages/index.tsx
TypeScript
import Link from 'next/link'
import React from 'react'
 
// MyButtonのpropsタイプを定義
interface MyButtonProps {
  onClick?: React.MouseEventHandler<HTMLAnchorElement>
  href?: string
}
 
// 転送されるrefを適切に型付けするためにReact.ForwardRefRenderFunctionを使用
const MyButton: React.ForwardRefRenderFunction<
  HTMLAnchorElement,
  MyButtonProps
> = ({ onClick, href }, ref) => {
  return (
    <a href={href} onClick={onClick} ref={ref}>
      Click Me
    </a>
  )
}
 
// React.forwardRefを使用してコンポーネントをラップ
const ForwardedMyButton = React.forwardRef(MyButton)
 
export default function Home() {
  return (
    <Link href="/about" passHref legacyBehavior>
      <ForwardedMyButton />
    </Link>
  )
}

URLオブジェクトを渡す

Link はURLオブジェクトを受け取ることもでき、自動的にフォーマットしてURL文字列を作成します:

pages/index.ts
TypeScript
import Link from 'next/link'
 
function Home() {
  return (
    <ul>
      <li>
        <Link
          href={{
            pathname: '/about',
            query: { name: 'test' },
          }}
        >
          About us
        </Link>
      </li>
      <li>
        <Link
          href={{
            pathname: '/blog/[slug]',
            query: { slug: 'my-post' },
          }}
        >
          Blog Post
        </Link>
      </li>
    </ul>
  )
}
 
export default Home

上記の例では、以下へのリンクがあります:

  • 事前に定義されたルート:/about?name=test
  • 動的ルート/blog/my-post

Node.js URLモジュールのドキュメントで定義されているすべてのプロパティを使用できます。

pushではなくURLを置き換える

Link コンポーネントのデフォルトの動作は、新しいURLを history スタックに push することです。以下の例のように、replace プロップを使用して新しいエントリーの追加を防ぐことができます:

pages/index.js
TypeScript
import Link from 'next/link'
 
export default function Home() {
  return (
    <Link href="/about" replace>
      About us
    </Link>
  )
}

ページ先頭へのスクロールを無効にする

Link のデフォルトの動作は、ページの先頭にスクロールすることです。ハッシュが定義されている場合、通常の <a> タグのように特定のIDにスクロールします。先頭/ハッシュへのスクロールを防ぐには、Linkscroll={false} を追加できます:

pages/index.tsx
TypeScript
import Link from 'next/link'
 
export default function Home() {
  return (
    <Link href="/#hashid" scroll={false}>
      先頭へのスクロールを無効にする
    </Link>
  )
}

Middlewareでのリンクのプリフェッチ

Middlewareを認証やその他の目的で使用し、ユーザーを別のページにリダイレクトすることは一般的です。Middlewareを介してリダイレクトされるリンクを <Link /> コンポーネントが適切にプリフェッチするには、Next.jsに表示するURLとプリフェッチするURLの両方を伝える必要があります。これは、プリフェッチする正しいルートを知るためにミドルウェアへの不要なフェッチを避けるために必要です。

例えば、認証済みユーザーとビジター用のビューを持つ /dashboard ルートを提供したい場合、Middlewareに以下を追加して、ユーザーを正しいページにリダイレクトできます:

middleware.ts
TypeScript
import { NextResponse } from 'next/server'
 
export function middleware(request: Request) {
  const nextUrl = request.nextUrl
  if (nextUrl.pathname === '/dashboard') {
    if (request.cookies.authToken) {
      return NextResponse.rewrite(new URL('/auth/dashboard', request.url))
    } else {
      return NextResponse.rewrite(new URL('/public/dashboard', request.url))
    }
  }
}

この場合、<Link /> コンポーネントで以下のコードを使用します:

pages/index.tsx
TypeScript
'use client'
 
import Link from 'next/link'
import useIsAuthed from './hooks/useIsAuthed' // 認証用のフック
 
export default function Home() {
  const isAuthed = useIsAuthed()
  const path = isAuthed ? '/auth/dashboard' : '/public/dashboard'
  return (
    <Link as="/dashboard" href={path}>
      Dashboard
    </Link>
  )
}

補足: 動的ルートを使用している場合は、ashref プロップを適応させる必要があります。例えば、middlewareを介して異なる方法で表示したい /dashboard/authed/[user] のような動的ルートがある場合、以下のように記述します:<Link href={{ pathname: '/dashboard/authed/[user]', query: { user: username } }} as="/dashboard/[user]">Profile</Link>

バージョン履歴

バージョン変更点
v13.0.0子要素の <a> タグが不要になりました。コードベースを自動的に更新するcodemodが提供されています。
v10.0.0動的ルートを指す href プロップは自動的に解決され、as プロップが不要になりました。
v8.0.0プリフェッチのパフォーマンスが向上しました。
v1.0.0next/link が導入されました。

ナビゲーションのブロック

特定の条件が満たされた場合、例えばフォームに未保存の変更がある場合など、ナビゲーションをブロックするために onNavigate プロパティを使用できます。アプリケーションの複数のコンポーネントでナビゲーションをブロックする必要がある場合(フォームの編集中に任意のリンクからのナビゲーションを防止するなど)、React Contextを使用することでこのブロック状態を共有する方法がクリーンです。まず、ナビゲーションブロック状態を追跡するためのコンテキストを作成します:

app/contexts/navigation-blocker.tsx
TypeScript
'use client'
 
import { createContext, useState, useContext } from 'react'
 
interface NavigationBlockerContextType {
  isBlocked: boolean
  setIsBlocked: (isBlocked: boolean) => void
}
 
export const NavigationBlockerContext =
  createContext<NavigationBlockerContextType>({
    isBlocked: false,
    setIsBlocked: () => {},
  })
 
export function NavigationBlockerProvider({
  children,
}: {
  children: React.ReactNode
}) {
  const [isBlocked, setIsBlocked] = useState(false)
 
  return (
    <NavigationBlockerContext.Provider value={{ isBlocked, setIsBlocked }}>
      {children}
    </NavigationBlockerContext.Provider>
  )
}
 
export function useNavigationBlocker() {
  return useContext(NavigationBlockerContext)
}

コンテキストを使用するフォームコンポーネントを作成します:

app/components/form.tsx
TypeScript
'use client'
 
import { useNavigationBlocker } from '../contexts/navigation-blocker'
 
export default function Form() {
  const { setIsBlocked } = useNavigationBlocker()
 
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault()
        setIsBlocked(false)
      }}
      onChange={() => setIsBlocked(true)}
    >
      <input type="text" name="name" />
      <button type="submit">Save</button>
    </form>
  )
}

ナビゲーションをブロックするカスタムLinkコンポーネントを作成します:

app/components/custom-link.tsx
TypeScript
'use client'
 
import Link from 'next/link'
import { useNavigationBlocker } from '../contexts/navigation-blocker'
 
interface CustomLinkProps extends React.ComponentProps<typeof Link> {
  children: React.ReactNode
}
 
export function CustomLink({ children, ...props }: CustomLinkProps) {
  const { isBlocked } = useNavigationBlocker()
 
  return (
    <Link
      onNavigate={(e) => {
        if (
          isBlocked &&
          !window.confirm('未保存の変更があります。それでも移動しますか?')
        ) {
          e.preventDefault()
        }
      }}
      {...props}
    >
      {children}
    </Link>
  )
}

ナビゲーションコンポーネントを作成します:

app/components/nav.tsx
TypeScript
'use client'
 
import { CustomLink as Link } from './custom-link'
 
export default function Nav() {
  return (
    <nav>
      <Link href="/">Home</Link>
      <Link href="/about">About</Link>
    </nav>
  )
}

最後に、ルートレイアウトでアプリを NavigationBlockerProvider でラップし、ページで各コンポーネントを使用します:

app/layout.tsx
TypeScript
import { NavigationBlockerProvider } from './contexts/navigation-blocker'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <NavigationBlockerProvider>{children}</NavigationBlockerProvider>
      </body>
    </html>
  )
}

そして、ページで NavForm コンポーネントを使用します:

app/page.tsx
TypeScript
import Nav from './components/nav'
import Form from './components/form'
 
export default function Page() {
  return (
    <div>
      <Nav />
      <main>
        <h1>Welcome to the Dashboard</h1>
        <Form />
      </main>
    </div>
  )
}

ユーザーがフォームに未保存の変更がある状態で CustomLink を使用してナビゲートしようとすると、移動前に確認が求められます。

バージョン履歴

バージョン変更内容
導入時期:v15.3.0onNavigate APIの追加
導入時期:v13.0.0子要素の <a> タグが不要になりました。コードベースを自動的に更新するためのcodemodが提供されています。
導入時期:v10.0.0動的ルートを指す href プロパティは自動的に解決され、as プロパティが不要になりました。
導入時期:v8.0.0プリフェッチのパフォーマンスが向上しました。
導入時期:v1.0.0next/link が導入されました。