メインコンテンツまでスキップ

<Form>

<Form> コンポーネントは、HTMLの <form> 要素を拡張し、事前取得 (prefetching)読み込み中のUIを提供し、送信時のクライアントサイドナビゲーションプログレッシブエンハンスメントを提供します。

これは、URLの検索パラメータを更新するフォームに便利で、上記を達成するために必要なボイラープレートコードを削減します。

基本的な使い方:

/app/ui/search.tsx
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>
)
}

参照

<Form> コンポーネントの動作は、action プロパティに string が渡されるか function が渡されるかに依存します。

  • action文字列の場合、<Form>はネイティブのHTMLフォームのように**GET**メソッドを使用して動作します。フォームデータはURLに検索パラメータとしてエンコードされ、フォームが送信されると指定されたURLに移動します。さらに、Next.jsでは次のことが行われます:
    • フォームが表示されるときにそのパスを事前取得 (prefetching) し、共有UI(例:layout.jsloading.js)をプリロードし、より高速なナビゲーションを実現します;
    • フォームが送信されたとき、ページ全体のリロードではなくクライアントサイドナビゲーションを行います。これにより、共有UIとクライアントサイドの状態が保持されます;
  • action関数(Server Action)の場合、<Form>Reactフォーム のように動作し、フォームが送信されたときにそのアクションを実行します;

action (文字列) のプロパティ

action が文字列のとき、<Form> コンポーネントは以下のプロパティをサポートします:

プロパティ必須
actionaction="/search"string (URLまたは相対パス)Yes
replacereplace={false}boolean-
scrollscroll={true}boolean-
prefetchprefetch={true}boolean-
  • action: フォームが送信されたときに移動するURLまたはパス;
    • 空の文字列 "" は、検索パラメータが更新された同ルートに移動します;
  • replace: ブラウザの履歴スタックに新しい履歴をプッシュする代わりに、現在の履歴状態を置き換えます; デフォルトは falseです;
  • scroll: ナビゲーション中のスクロール動作を制御します; デフォルトで true になっており、新しいルートのトップにスクロールし、前後のナビゲーションでスクロール位置が維持されます;
  • prefetch: フォームがユーザーのビューポートに表示されたときにそのパスを事前取得 (prefetch) するかどうかを制御します; デフォルトは trueです;

action (関数) のプロパティ

action が関数のとき、<Form> コンポーネントは以下のプロパティをサポートします:

プロパティ必須
actionaction={myAction}function (Server Action)Yes
  • action: フォームが送信されたときに呼び出されるServer Action; 詳しくは Reactのドキュメント を参照してください;

知っておくと便利: action が関数の場合、 replace および scroll プロパティは無視されます;

注意事項

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

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

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 に空の文字列""を渡すと、フォームは検索パラメータが更新された同じルートに移動します。

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

/app/search/page.tsx
import { getSearchResults } from '@/lib/search'

export default async function SearchPage({
searchParams,
}: {
searchParams: { [key: string]: string | string[] | undefined }
}) {
const results = await getSearchResults(searchParams.query)

return <div>...</div>
}

<Form> がユーザーのビューポートに表示されると、/searchページ上の共有UI(例:layout.js および loading.js)が事前取得 (prefetching) されます。送信時に、フォームはすぐに新しいルートに移動し、結果が取得されている間、ロード中のUIが表示されます。loading.js を使用してフォールバックUIを設計できます:

/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/navigationredirect関数を使用して、新しい投稿ページにナビゲートできます。

知っておくと便利: フォーム送信の「宛先」はアクションが実行されるまで不明なため、<Form> は自動的に共有UIを事前取得 (prefetching) することができません。

/app/posts/actions.ts
'use server'
import { redirect } from 'next/navigation'

export async function createPost(formData: FormData) {
// 新しい投稿を作成する
// ...

// 新しい投稿にリダイレクトする
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 data = await getPost((await params).id)

return (
<div>
<h1>{data.title}</h1>
{/* ... */}
</div>
)
}

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