Form コンポーネント
<Form>
コンポーネントはHTMLの <form>
要素を拡張し、プリフェッチ と ローディングUI、 クライアントサイドナビゲーション、プログレッシブエンハンスメント を提供します。
URL検索パラメータを更新するフォームに特に有用で、上記機能を実現するために必要な定型コードを削減できます。
基本的な使い方:
import Form from 'next/form'
export default function Page() {
return (
<Form action="/search">
{/* 送信時、入力値がURLに追加されます
例: /search?query=abc */}
<input name="query" />
<button type="submit">Submit</button>
</Form>
)
}
import Form from 'next/form'
export default function Search() {
return (
<Form action="/search">
{/* 送信時、入力値がURLに追加されます
例: /search?query=abc */}
<input name="query" />
<button type="submit">Submit</button>
</Form>
)
}
リファレンス
<Form>
コンポーネントの動作は、action
プロパティに渡される値が string
か function
かによって異なります。
action
が 文字列 の場合、<Form>
はGET
メソッドを使用するネイティブHTMLフォームのように動作します。フォームデータはURLの検索パラメータとしてエンコードされ、フォーム送信時に指定されたURLにナビゲートします。さらにNext.jsは:- フォームが表示されるときにパスをプリフェッチし、共有UI(例:
layout.js
やloading.js
)を事前読み込みしてナビゲーションを高速化します。 - フォーム送信時にクライアントサイドナビゲーションを実行し、ページ全体のリロードを防ぎます。これにより共有UIとクライアントサイドの状態が保持されます。
- フォームが表示されるときにパスをプリフェッチし、共有UI(例:
action
が 関数(サーバーアクション)の場合、<Form>
はReactフォームのように動作し、フォーム送信時にアクションを実行します。
action
(文字列) プロパティ
action
が文字列の場合、<Form>
コンポーネントは以下のプロパティをサポートします:
プロパティ | 例 | 型 | 必須 |
---|---|---|---|
action | action="/search" | string (URLまたは相対パス) | はい |
replace | replace={false} | boolean | - |
scroll | scroll={true} | boolean | - |
prefetch | prefetch={true} | boolean | - |
action
: フォーム送信時にナビゲートするURLまたはパス。- 空文字列
""
を指定すると、同じルートに検索パラメータを更新してナビゲートします。
- 空文字列
replace
: 新しい履歴を追加する代わりに、現在の履歴状態を置き換えます。デフォルトはfalse
。scroll
: ナビゲーション時のスクロール動作を制御します。デフォルトはtrue
で、新しいルートの先頭にスクロールし、前後のナビゲーションでスクロール位置を保持します。prefetch
: フォームがユーザーのビューポートに表示されたときにパスをプリフェッチするかどうかを制御します。デフォルトはtrue
。
action
(関数) プロパティ
action
が関数の場合、<Form>
コンポーネントは以下のプロパティをサポートします:
プロパティ | 例 | 型 | 必須 |
---|---|---|---|
action | action={myAction} | function (サーバーアクション) | はい |
action
: フォーム送信時に呼び出されるサーバーアクション。詳細はReactドキュメントを参照してください。
補足:
action
が関数の場合、replace
とscroll
プロパティは無視されます。
注意点
formAction
:<button>
または<input type="submit">
フィールドで使用し、action
プロパティを上書きできます。Next.jsはクライアントサイドナビゲーションを実行しますが、この方法ではプリフェッチはサポートされません。basePath
を使用する場合、formAction
パスにも含める必要があります。例:formAction="/base-path/search"
。
key
: 文字列action
にkey
プロパティを渡すことはサポートされていません。再レンダリングをトリガーしたり、ミューテーションを実行したりする場合は、代わりに関数action
の使用を検討してください。
onSubmit
: フォーム送信ロジックを処理するために使用できます。ただし、event.preventDefault()
を呼び出すと、指定されたURLへのナビゲーションなど<Form>
の動作が上書きされます。method
,encType
,target
: これらは<Form>
の動作を上書きするためサポートされていません。- 同様に、
formMethod
、formEncType
、formTarget
はそれぞれmethod
、encType
、target
プロパティを上書きするために使用できますが、これらを使用するとネイティブのブラウザ動作にフォールバックします。 - これらのプロパティが必要な場合は、代わりにHTMLの
<form>
要素を使用してください。
- 同様に、
<input type="file">
:action
が文字列の場合、この入力タイプを使用すると、ファイルオブジェクトではなくファイル名が送信されるというブラウザの動作に従います。
使用例
検索結果ページに移動する検索フォーム
action
にパスを渡すことで、検索結果ページにナビゲートする検索フォームを作成できます:
import Form from 'next/form'
export default function Page() {
return (
<Form action="/search">
<input name="query" />
<button type="submit">Submit</button>
</Form>
)
}
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
に空文字列""
を渡すと、フォームは同じルートに検索パラメータを更新してナビゲートします。
結果ページでは、searchParams
page.js
プロパティを使用してクエリにアクセスし、外部ソースからデータを取得できます。
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>
}
import { getSearchResults } from '@/lib/search'
export default async function SearchPage({ searchParams }) {
const results = await getSearchResults((await searchParams).query)
return <div>...</div>
}
<Form>
がユーザーのビューポートに表示されると、/search
ページの共有UI(layout.js
や loading.js
など)がプリフェッチされます。送信時、フォームはすぐに新しいルートにナビゲートし、結果が取得される間はローディングUIが表示されます。loading.js
を使用してフォールバックUIを設計できます:
export default function Loading() {
return <div>Loading...</div>
}
export default function Loading() {
return <div>Loading...</div>
}
共有UIがまだ読み込まれていない場合に備えて、useFormStatus
を使用してユーザーに即時フィードバックを表示できます。
まず、フォームが保留中の場合にローディング状態を表示するコンポーネントを作成します:
次に、検索フォームページを更新して SearchButton
コンポーネントを使用します:
import Form from 'next/form'
import { SearchButton } from '@/ui/search-button'
export default function Page() {
return (
<Form action="/search">
<input name="query" />
<SearchButton />
</Form>
)
}
サーバーアクションによるミューテーション
action
プロパティに関数を渡すことで、ミューテーションを実行できます。
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>
)
}
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を自動的にプリフェッチできません。
'use server'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
// 新しい投稿を作成
// ...
// 新しい投稿にリダイレクト
redirect(`/posts/${data.id}`)
}
'use server'
import { redirect } from 'next/navigation'
export async function createPost(formData) {
// 新しい投稿を作成
// ...
// 新しい投稿にリダイレクト
redirect(`/posts/${data.id}`)
}
新しいページでは、params
プロパティを使用してデータを取得できます:
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>
)
}
import { getPost } from '@/posts/data'
export default async function PostPage({ params }) {
const { id } = await params
const data = await getPost(id)
return (
<div>
<h1>{data.title}</h1>
{/* ... */}
</div>
)
}
その他の例についてはサーバーアクションのドキュメントを参照してください。