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

Form

<Form>コンポーネントは、HTMLの<form>要素を拡張して、プリフェッチを提供し、ローディング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 propにstringまたはfunctionが渡されるかどうかによって異なります。

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

action(string)Props

actionがstringの場合、<Form>コンポーネントは次のpropsをサポートします:

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

action(function)Props

actionがfunctionの場合、<Form>コンポーネントは次のpropをサポートします:

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

Good to knowactionがfunctionの場合、replacescroll propsは無視されます。

注意点

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

Good to knowactionに空の文字列""を渡すと、フォームは更新された検索パラメータで同じルートに移動します。

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

/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.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>
)
}
/app/ui/search-button.js
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に関数を渡すことで、ミューテーションを実行できます。

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

Good to know:フォーム送信の「宛先」はアクションが実行されるまで不明であるため、<Form>は自動的に共有UIをプリフェッチできません。

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

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

// 新しい投稿にリダイレクト
redirect(`/posts/${data.id}`)
}

次に、新しいページでparams propを使用してデータを取得できます:

/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ドキュメントを参照してください。