コンテンツにスキップ

Form Component

<Form> コンポーネントは、HTML の <form> 要素を拡張して、プリフェッチローディング UI 送信時のクライアントサイドナビゲーション、およびプログレッシブエンハンスメントを提供します。

URL の検索パラメータを更新するフォームに便利です。これにより、上記を実現するために必要な定型コードが削減されます。

基本的な使い方

/app/ui/search.tsx
import Form from 'next/form'
 
export default function Page() {
  return (
    <Form action="/search">
      {/* On submission, the input value will be appended to
          the URL, e.g. /search?query=abc */}
      <input name="query" />
      <button type="submit">Submit</button>
    </Form>
  )
}

リファレンス

<Form> コンポーネントの動作は、action プロパティに string または function が渡されるかによって異なります。

  • action が **string** の場合、<Form> はネイティブ HTML フォームのように動作し、**GET** メソッドを使用します。フォームデータは URL に検索パラメータとしてエンコードされ、フォームが送信されると指定された URL にナビゲートします。さらに、Next.js は
    • プリフェッチ フォームが表示されると、共有 UI (例: layout.js および loading.js) がプリロードされ、ナビゲーションが高速化されます。
    • フォームが送信されると、ページ全体のリロードではなく、クライアントサイドナビゲーション を実行します。これにより、共有 UI とクライアントサイドの状態が維持されます。
  • action が **function** (Server Action) の場合、<Form>React フォーム のように動作し、フォームが送信されるとアクションを実行します。

action (string) プロパティ

action が string の場合、<Form> コンポーネントは以下のプロパティをサポートします。

プロパティタイプ必須
actionaction="/search"string (URL または相対パス)はい
replacereplace={false}boolean-
scrollscroll={true}boolean-
prefetchprefetch={true}boolean-
  • action: フォーム送信時にナビゲートする URL またはパス。
    • 空文字列 "" は、検索パラメータを更新して同じルートにナビゲートします。
  • replace: 新しい履歴エントリをプッシュするのではなく、現在の履歴状態を置き換えます。デフォルトは false です。
  • scroll: ナビゲーション中のスクロール動作を制御します。デフォルトは true で、新しいルートの先頭にスクロールし、戻る/進むナビゲーションのスクロール位置を維持します。
  • prefetch: フォームがユーザーのビューポートに表示されるときにパスがプリフェッチされるかどうかを制御します。デフォルトは true です。

action (function) プロパティ

action が function の場合、<Form> コンポーネントは以下のプロパティをサポートします。

プロパティタイプ必須
actionaction={myAction}function (Server Action)はい
  • action: フォーム送信時に呼び出される Server Action。詳細については、React ドキュメント を参照してください。

知っておくと良いこと: action が function の場合、replace および scroll プロパティは無視されます。

注意点

  • formAction: action プロパティをオーバーライドするために <button> または <input type="submit"> フィールドで使用できます。Next.js はクライアントサイドナビゲーションを実行しますが、このアプローチではプリフェッチはサポートされません。
    • basePath を使用する場合、formAction パスにも含める必要があります。例: formAction="/base-path/search"
  • key: 文字列 actionkey プロパティを渡すことはサポートされていません。再レンダリングをトリガーしたり、ミューテーションを実行したりしたい場合は、代わりに function action の使用を検討してください。
  • onSubmit: フォーム送信ロジックを処理するために使用できます。ただし、event.preventDefault() を呼び出すと、指定された URL へのナビゲーションなどの <Form> の動作がオーバーライドされます。
  • method, encType, target: <Form> の動作をオーバーライドするため、サポートされていません。
    • 同様に、formMethodformEncTypeformTarget は、それぞれ methodencTypetarget プロパティをオーバーライドするために使用でき、それらを使用するとネイティブブラウザの動作にフォールバックします。
    • これらのプロパティを使用する必要がある場合は、代わりに HTML の <form> 要素を使用してください。
  • <input type="file">: action が string の場合にこの入力タイプを使用すると、ファイルオブジェクトではなくファイル名を送信することにより、ブラウザの動作と一致します。

検索結果ページに遷移する検索フォーム

パスを action として渡すことで、検索結果ページにナビゲートする検索フォームを作成できます。

/app/page.tsx
import Form from 'next/form'
 
export default function Page() {
  return (
    <Form action="/search">
      <input name="query" />
      <button type="submit">Submit</button>
    </Form>
  )
}

ユーザーがクエリ入力フィールドを更新してフォームを送信すると、フォームデータは URL に検索パラメータとしてエンコードされます。例: /search?query=abc

知っておくと良いこと: action に空文字列 "" を渡すと、フォームは検索パラメータを更新して同じルートにナビゲートします。

結果ページでは、page.jssearchParams プロパティを使用してクエリにアクセスし、外部ソースからデータを取得するために使用できます。

/app/search/page.tsx
import { getSearchResults } from '@/lib/search'
 
export default async function SearchPage({
  searchParams,
}: {
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
  const results = await getSearchResults((await searchParams).query)
 
  return <div>...</div>
}

<Form> がユーザーのビューポートに表示されると、/search ページの共有 UI (layout.jsloading.js など) がプリフェッチされます。送信時には、フォームはすぐに新しいルートにナビゲートし、結果が取得される間、ローディング UI を表示します。フォールバック UI は loading.js を使用して設計できます。

/app/search/loading.tsx
export default function Loading() {
  return <div>Loading...</div>
}

共有 UI がまだロードされていないケースに対応するために、useFormStatus を使用して、ユーザーに即座にフィードバックを表示できます。

まず、フォームが保留中の場合にローディング状態を表示するコンポーネントを作成します。

/app/ui/search-button.tsx
'use client'
import { useFormStatus } from 'react-dom'
 
export default function SearchButton() {
  const status = useFormStatus()
  return (
    <button type="submit">{status.pending ? 'Searching...' : 'Search'}</button>
  )
}

次に、検索フォームページを更新して SearchButton コンポーネントを使用します。

/app/page.tsx
import Form from 'next/form'
import { SearchButton } from '@/ui/search-button'
 
export default function Page() {
  return (
    <Form action="/search">
      <input name="query" />
      <SearchButton />
    </Form>
  )
}

Server Actions を使用したミューテーション

action プロパティに関数を渡すことで、ミューテーションを実行できます。

/app/posts/create/page.tsx
import Form from 'next/form'
import { createPost } from '@/posts/actions'
 
export default function Page() {
  return (
    <Form action={createPost}>
      <input name="title" />
      {/* ... */}
      <button type="submit">Create Post</button>
    </Form>
  )
}

ミューテーション後、新しいリソースにリダイレクトするのが一般的です。next/navigation から redirect 関数を使用して、新しい投稿ページにナビゲートできます。

知っておくと良いこと: フォーム送信の「宛先」はアクションが実行されるまで不明なため、<Form> は共有 UI を自動的にプリフェッチできません。

/app/posts/actions.ts
'use server'
import { redirect } from 'next/navigation'
 
export async function createPost(formData: FormData) {
  // Create a new post
  // ...
 
  // Redirect to the new post
  redirect(`/posts/${data.id}`)
}

その後、新しいページで params プロパティを使用してデータを取得できます。

/app/posts/[id]/page.tsx
import { getPost } from '@/posts/data'
 
export default async function PostPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const data = await getPost(id)
 
  return (
    <div>
      <h1>{data.title}</h1>
      {/* ... */}
    </div>
  )
}

他の例については、Server Actions のドキュメントを参照してください。