コンテンツにスキップ

Link

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

基本的な使い方

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

リファレンス

<Link>コンポーネントに渡すことができるpropsは以下の通りです

Prop必須
hrefhref="/dashboard"文字列またはオブジェクトはい
replacereplace={false}真偽値-
scrollscroll={false}真偽値-
prefetchprefetch={false}真偽値-
legacyBehaviorlegacyBehavior={true}真偽値-
passHrefpassHref={true}真偽値-
shallowshallow={false}真偽値-
localelocale="fr"文字列または真偽値-
onNavigateonNavigate={(e) => {}}関数-

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

href (必須)

ナビゲート先のパスまたはURLです。

pages/index.tsx
import Link from 'next/link'
 
// Navigate to /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
import Link from 'next/link'
 
export default function Home() {
  return (
    <Link href="/dashboard" replace>
      Dashboard
    </Link>
  )
}

scroll

デフォルトはtrueです。 Next.jsの<Link>のデフォルトのスクロール動作は、ブラウザが戻る/進むナビゲーションを処理するのと同様に、スクロール位置を維持することです。新しいページにナビゲートすると、ページがビューポートに表示されている限りスクロール位置は変わりません。ただし、ページがビューポートに表示されていない場合、Next.jsは最初のページ要素の最上部までスクロールします。

scroll = {false}の場合、Next.jsは最初のページ要素までスクロールしようとしません。

豆知識: Next.jsはスクロール動作を管理する前にscroll: falseかどうかをチェックします。スクロールが有効になっている場合、ナビゲーションに関連するDOMノードを識別し、各トップレベル要素を検査します。スクロールできない要素やレンダリングされたHTMLがない要素はすべてスキップされます。これには、スティッキーまたは固定位置の要素、およびgetBoundingClientRectで計算された非表示の要素が含まれます。その後、Next.jsはビューポートに表示されているスクロール可能な要素を識別するまで兄弟要素を続行します。

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

prefetch

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

prefetch propに渡すことができる値は以下の通りです。

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

legacyBehavior

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

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

passHref

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

shallow

getStaticPropsgetServerSideProps、またはgetInitialPropsを再実行せずに、現在のページのパスを更新します。デフォルトはfalseです。

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

locale

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

pages/index.tsx
import Link from 'next/link'
 
export default function Home() {
  return (
    <>
      {/* Default behavior: locale is prepended */}
      <Link href="/dashboard">Dashboard (with locale)</Link>
 
      {/* Disable locale prepending */}
      <Link href="/dashboard" locale={false}>
        Dashboard (without locale)
      </Link>
 
      {/* Specify a different locale */}
      <Link href="/dashboard" locale="fr">
        Dashboard (French)
      </Link>
    </>
  )
}

onNavigate

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

app/page.tsx
import Link from 'next/link'
 
export default function Page() {
  return (
    <Link
      href="/dashboard"
      onNavigate={(e) => {
        // Only executes during SPA navigation
        console.log('Navigating...')
 
        // Optionally prevent navigation
        // e.preventDefault()
      }}
    >
      Dashboard
    </Link>
  )
}

豆知識: onClickonNavigateは似ているように見えますが、それぞれ異なる目的を果たします。onClickはすべてのクリックイベントで実行されますが、onNavigateはクライアントサイドのナビゲーション中のみ実行されます。主な違いをいくつか挙げます。

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

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

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

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

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

pages/blog/index.tsx
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
import Link from 'next/link'
import styled from 'styled-components'
 
// This creates a custom component that wraps an <a> tag
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プラグマ機能(@jsx jsx)を使用している場合、<a>タグを直接使用している場合でもpassHrefを使用する必要があります。
  • コンポーネントは、ナビゲーションを正しくトリガーするためにonClickプロパティをサポートする必要があります。

関数コンポーネントのネスト

Linkの子要素が関数コンポーネントである場合、passHreflegacyBehaviorを使用するだけでなく、コンポーネントをReact.forwardRefでラップする必要があります。

pages/index.tsx
import Link from 'next/link'
import React from 'react'
 
// Define the props type for MyButton
interface MyButtonProps {
  onClick?: React.MouseEventHandler<HTMLAnchorElement>
  href?: string
}
 
// Use React.ForwardRefRenderFunction to properly type the forwarded ref
const MyButton: React.ForwardRefRenderFunction<
  HTMLAnchorElement,
  MyButtonProps
> = ({ onClick, href }, ref) => {
  return (
    <a href={href} onClick={onClick} ref={ref}>
      Click Me
    </a>
  )
}
 
// Use React.forwardRef to wrap the component
const ForwardedMyButton = React.forwardRef(MyButton)
 
export default function Home() {
  return (
    <Link href="/about" passHref legacyBehavior>
      <ForwardedMyButton />
    </Link>
  )
}

URLオブジェクトの渡し方

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

pages/index.ts
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 propを使用できます。

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

ページ上部へのスクロールを無効にする

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

pages/index.tsx
import Link from 'next/link'
 
export default function Home() {
  return (
    <Link href="/#hashid" scroll={false}>
      Disables scrolling to the top
    </Link>
  )
}

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

例えば、認証済みビューと訪問者ビューを持つ/dashboardルートを提供したい場合、ミドルウェアに以下を追加して、ユーザーを正しいページにリダイレクトできます。

middleware.ts
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
'use client'
 
import Link from 'next/link'
import useIsAuthed from './hooks/useIsAuthed' // Your auth hook
 
export default function Home() {
  const isAuthed = useIsAuthed()
  const path = isAuthed ? '/auth/dashboard' : '/public/dashboard'
  return (
    <Link as="/dashboard" href={path}>
      Dashboard
    </Link>
  )
}

豆知識: 動的ルートを使用している場合、ashrefのpropsを調整する必要があります。例えば、/dashboard/authed/[user]のような動的ルートがあり、ミドルウェア経由で異なる表示をしたい場合、<Link href={{ pathname: '/dashboard/authed/[user]', query: { user: username } }} as="/dashboard/[user]">Profile</Link>と記述します。

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

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

app/contexts/navigation-blocker.tsx
'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
'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
'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('You have unsaved changes. Leave anyway?')
        ) {
          e.preventDefault()
        }
      }}
      {...props}
    >
      {children}
    </Link>
  )
}

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

app/components/nav.tsx
'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
import { NavigationBlockerProvider } from './contexts/navigation-blocker'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <NavigationBlockerProvider>{children}</NavigationBlockerProvider>
      </body>
    </html>
  )
}

次に、ページでNavおよびFormコンポーネントを使用します。

app/page.tsx
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 propsは自動的に解決され、as propは不要になりました。
v8.0.0プリフェッチのパフォーマンスを改善。
v1.0.0next/linkが導入されました。