Menu

Form コンポーネント

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

URL 検索パラメータを更新するフォームに役立ちます。上記を実現するために必要なボイラープレートコードを削減できます。

基本的な使用方法:

/app/ui/search.tsx
TypeScript
import Form from 'next/form'
 
export default function Page() {
  return (
    <Form action="/search">
      {/* 送信時、入力値が URL に追加されます。
          例:/search?query=abc */}
      <input name="query" />
      <button type="submit">送信</button>
    </Form>
  )
}

リファレンス

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

  • action文字列の場合、<Form>GET メソッドを使用するネイティブ HTML フォームのように動作します。フォームデータは URL に検索パラメータとしてエンコードされ、フォーム送信時に指定された URL にナビゲートします。さらに、Next.js は以下を行います:
    • プリフェッチを実行します。フォームがビューポートに表示されたとき、共有 UI(例:layout.jsloading.js)を事前読み込みし、より高速なナビゲーションを実現します。
    • フォーム送信時に完全なページ再読み込みの代わりにクライアント側ナビゲーションを実行します。これにより、共有 UI とクライアント側の状態を保持します。
  • action関数(Server Action)の場合、<Form>React フォームのように動作し、フォーム送信時にアクションを実行します。

action(文字列)プロップ

action が文字列の場合、<Form> コンポーネントは以下のプロップをサポートします:

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

action(関数)プロップ

action が関数の場合、<Form> コンポーネントは以下のプロップをサポートします:

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

補足action が関数の場合、replacescroll プロップは無視されます。

注意事項

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

検索結果ページにリードする検索フォーム

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

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

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

補足action に空の文字列 "" を渡すと、フォームは同じルートに検索パラメータを更新してナビゲートします。

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

/app/search/page.tsx
TypeScript
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 など)がプリフェッチされます。送信時、フォームは新しいルートにすぐにナビゲートし、結果を取得中に loading UI を表示します。loading.js を使用してフォールバック UI を設計できます:

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

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

まず、フォームが保留中のときに読込状態を表示するコンポーネントを作成します:

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

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

/app/page.tsx
TypeScript
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
TypeScript
import Form from 'next/form'
import { createPost } from '@/posts/actions'
 
export default function Page() {
  return (
    <Form action={createPost}>
      <input name="title" />
      {/* ... */}
      <button type="submit">投稿を作成</button>
    </Form>
  )
}

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

補足:フォーム送信の「送信先」はアクション実行まで不明なため、<Form> は共有 UI を自動的にプリフェッチできません。

/app/posts/actions.ts
TypeScript
'use server'
import { redirect } from 'next/navigation'
 
export async function createPost(formData: FormData) {
  // 新しい投稿を作成
  // ...
 
  // 新しい投稿にリダイレクト
  redirect(`/posts/${data.id}`)
}

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

/app/posts/[id]/page.tsx
TypeScript
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 ドキュメントを参照してください。