Form
<Form>コンポーネントは、HTMLの<form>要素を拡張して、プリフェッチされたローディングUI、送信時のクライアントサイドナビゲーション、およびプログレッシブエンハンスメントを提供します。
これは、URLの検索パラメータを更新するフォームに便利で、上記を達成するために必要なボイラープレートコードを削減します。
基本的な使い方:
- TypeScript
- JavaScript
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 propにstringまたはfunctionが渡されるかどうかによって異なります。
actionがstringの場合、<Form>は**GET**メソッドを使用するネイティブHTMLフォームのように動作します。フォームデータは検索パラメータとしてURLにエンコードされ、フォームが送信されると指定されたURLに移動します。さらに、Next.jsは以下を行います:- フォームが表示されるときにパスをプリフェッチし、共有UI(例:
layout.jsやloading.js)を事前にロードすることで、ナビゲーションを高速化します。 - フォームが送信されるときに、完全なページリロードの代わりにクライアントサイドナビゲーションを実行します。これにより、共有UIとクライアントサイドの状態が保持されます。
- フォームが表示されるときにパスをプリフェッチし、共有UI(例:
actionがfunction(Server Action)の場合、<Form>はReactフォームのように動作し、フォームが送信されるとアクションを実行します。
action(string)Props
actionがstringの場合、<Form>コンポーネントは以下のpropsをサポートします:
| Prop | Example | Type | Required |
|---|---|---|---|
action | action="/search" | string (URLまたは相対パス) | Yes |
replace | replace={false} | boolean | - |
scroll | scroll={true} | boolean | - |
prefetch | prefetch={true} | boolean | - |
action: フォームが送信されたときに移動するURLまたはパス- 空の文字列
""は、更新された検索パラメータで同じルートに移動します。
- 空の文字列
replace: ブラウザの履歴スタックに新しい履歴をプッシュする代わりに、現在の履歴状態を置き換えます。デフォルトはfalseです。scroll: ナビゲーション中のスクロール動作を制御します。デフォルトはtrueで、新しいルートのトップにスクロールし、前後のナビゲーションでスクロール位置を維持します。prefetch: フォームがユーザーのビューポートに表示されたときにパスをプリフェッチするかどうかを制御します。デフォルトはtrueです。
action(function)Props
actionがfunctionの場合、<Form>コンポーネントは以下のpropをサポートします:
| Prop | Example | Type | Required |
|---|---|---|---|
action | action={myAction} | function (Server Action) | Yes |
action: フォームが送信されたときに呼び出されるServer Action。詳細はReactドキュメントを参照してください。
Good to know:
actionがfunctionの場合、replaceとscrollのpropsは無視されます。
注意点
formAction:<button>または<input type="submit">フィールドでactionpropをオーバーライドするために使用できます。Next.jsはクライアントサイドナビゲーションを実行しますが、このアプローチはプリフェッチをサポートしていません。basePathを使用する場合、formActionパスにもそれを含める必要があります。例:formAction="/base-path/search"。
key: stringactionにkeypropを渡すことはサポートされていません。再レンダリングをトリガーしたり、ミューテーションを実行したりしたい場合は、functionactionを使用することを検討してください。
onSubmit: フォーム送信ロジックを処理するために使用できます。ただし、event.preventDefault()を呼び出すと、指定されたURLへのナビゲーションなどの<Form>の動作がオーバーライドされます。method,encType,target:<Form>の動作をオーバーライドするためサポートされていません。- 同様に、
formMethod、formEncType、およびformTargetを使用してmethod、encType、およびtargetのpropsをオーバーライドすることができ、それらを使用するとネイティブブラウザの動作にフォールバックします。 - これらのpropsを使用する必要がある場合は、HTMLの
<form>要素を使用してください。
- 同様に、
<input type="file">:actionがstringの場合、この入力タイプを使用すると、ファイルオブジェクトではなくファイル名を送信することでブラウザの動作に一致します。
例
検索結果ページに移動する検索フォーム
パスをactionとして渡すことで、検索結果ページに移動する検索フォームを作成できます:
- TypeScript
- JavaScript
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
Good to know:
actionに空の文字列""を渡すと、フォームは更新された検索パラメータで同じルートに移動します。
結果ページでは、searchParams page.js propを使用してクエリにアクセスし、外部ソースからデータを取得できます。
- TypeScript
- JavaScript
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を表示します。フォールバックUIはloading.jsを使用して設計できます:
- TypeScript
- JavaScript
export default function Loading() {
return <div>Loading...</div>
}
export default function Loading() {
return <div>Loading...</div>
}
共有UIがまだロードされていない場合に備えて、useFormStatusを使用してユーザーに即時フィードバックを表示できます。
まず、フォームが保留中のときにローディング状態を表示するコンポーネントを作成します:
- TypeScript
- JavaScript
'use client'
import { useFormStatus } from 'react-dom'
export default function SearchButton() {
const status = useFormStatus()
return (
<button type="submit">{status.pending ? 'Searching...' : 'Search'}</button>
)
}
'use client'
import { useFormStatus } from 'react-dom'
export default function SearchButton() {
const status = useFormStatus()
return (
<button type="submit">{status.pending ? 'Searching...' : 'Search'}</button>
)
}
次に、SearchButtonコンポーネントを使用するように検索フォームページを更新します:
- 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>
)
}
- JavaScript
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 propに関数を渡すことで、ミューテーションを実行できます。
- TypeScript
- JavaScript
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>
)
}
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関数を使用して、新しい投稿ページにナビゲートできます。
Good to know: フォーム送信の「宛先」はアクションが実行されるまで不明であるため、
<Form>は自動的に共有UIをプリフェッチできません。
- TypeScript
- JavaScript
'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 propを使用してデータを取得できます:
- TypeScript
- JavaScript
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>
)
}
詳細な例については、Server Actionsドキュメントを参照してください。