コンテンツへスキップ

フォーム

<Form>コンポーネントはHTMLの<form>要素を拡張し、プリフェッチによるローディングUI 送信時のクライアントサイドナビゲーション、そしてプログレッシブエンハンスメントを提供します。

これは、URLの検索パラメータを更新するフォームに役立ちます。上記の機能を実現するために必要な定型コードを減らすことができるためです。

基本的な使用法

/app/ui/search.tsx
import Form from 'next/form'
 
export default function Page() {
  return (
    <Form action="/search">
      {/* On submission, the input value will be appended to
          the URL, e.g. /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.jsloading.js) をプリロードすることで、ナビゲーションを高速化します。
    • フォームが送信されると、ページ全体の再ロードではなく、クライアントサイドナビゲーションを実行します。これにより、共有UIとクライアントサイドの状態が維持されます。
  • action関数(サーバーアクション)の場合、<Form>Reactフォームのように動作し、フォームが送信されるとアクションを実行します。

action (文字列) Props

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

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

action (関数) Props

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

プロップ必須
actionaction={myAction}function (サーバーアクション)はい
  • action: フォームが送信されたときに呼び出されるサーバーアクション。詳細については、Reactのドキュメントを参照してください。

豆知識: actionが関数の場合、replacescrollプロップは無視されます。

注意点

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

ユーザーのビューポートに<Form>が表示されると、/searchページの共有UI(layout.jsloading.jsなど)がプリフェッチされます。送信時には、フォームはすぐに新しいルートにナビゲートし、結果がフェッチされている間、ローディング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>
  )
}

サーバーアクションによるミューテーション

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を自動的にプリフェッチできません。

/app/posts/actions.ts
'use server'
import { redirect } from 'next/navigation'
 
export async function createPost(formData: FormData) {
  // Create a new post
  // ...
 
  // Redirect to the new post
  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 { id } = await params
  const data = await getPost(id)
 
  return (
    <div>
      <h1>{data.title}</h1>
      {/* ... */}
    </div>
  )
}

詳細な例については、サーバーアクションのドキュメントを参照してください。