Form 表單元件

<Form> 元件擴展了 HTML <form> 元素的功能,提供 預取 (prefetching) 載入 UI 客戶端導航 (client-side navigation) 提交和 漸進增強 (progressive enhancement)

對於需要更新 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>
  )
}

參考說明

<Form> 元件的行為取決於 action 屬性傳遞的是 string 還是 function

  • action字串 時,<Form> 的行為類似於使用 GET 方法的原生 HTML 表單。表單數據會被編碼為 URL 搜尋參數,提交時會導航到指定 URL。此外,Next.js 還會:
  • action函數(伺服器動作 Server Action)時,<Form> 的行為類似於 React 表單,在表單提交時執行該動作。

action (字串) 屬性

action 是字串時,<Form> 元件支援以下屬性:

屬性範例類型必填
actionaction="/search"string (URL 或相對路徑)
replacereplace={false}boolean-
scrollscroll={true}boolean-
prefetchprefetch={true}boolean-
  • action:表單提交時要導航到的 URL 或路徑。
    • 空字串 "" 會導航到相同路由並更新搜尋參數。
  • replace:替換當前歷史狀態,而非在瀏覽器歷史記錄堆疊中新增一筆。預設為 false
  • scroll:控制導航時的滾動行為。預設為 true,表示會滾動到新路由的頂部,並在後退和前進導航時保持滾動位置。
  • prefetch:控制當表單在用戶視窗中可見時是否預取路徑。預設為 true

action (函數) 屬性

action 是函數時,<Form> 元件支援以下屬性:

屬性範例類型必填
actionaction={myAction}function (Server Action)
  • action:表單提交時要呼叫的伺服器動作 (Server Action)。詳見 React 文檔

須知:當 action 是函數時,replacescroll 屬性會被忽略。

注意事項

  • formAction:可在 <button><input type="submit"> 欄位中使用,以覆蓋 action 屬性。Next.js 會執行客戶端導航,但此方法不支援預取。
    • 使用 basePath 時,必須在 formAction 路徑中包含它,例如 formAction="/base-path/search"
  • key:不支援將 key 屬性傳遞給字串 action。如需觸發重新渲染或執行變更,請考慮改用函數 action
  • onSubmit:可用於處理表單提交邏輯。但呼叫 event.preventDefault() 會覆蓋 <Form> 的行為,例如導航到指定 URL。
  • methodencTypetarget:不支援,因為它們會覆蓋 <Form> 的行為。
    • 類似地,formMethodformEncTypeformTarget 可用於分別覆蓋 methodencTypetarget 屬性,使用它們會回退到原生瀏覽器行為。
    • 如需使用這些屬性,請改用 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>
  )
}

當用戶更新查詢輸入欄位並提交表單時,表單數據將被編碼為 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>
}

<Form> 在用戶視窗中可見時,/search 頁面上的共享 UI(如 layout.jsloading.js)將被預取。提交時,表單會立即導航到新路由,並在獲取結果時顯示載入 UI。您可以使用 loading.js 設計回退 UI:

export default function Loading() {
  return <div>Loading...</div>
}

為了涵蓋共享 UI 尚未載入的情況,您可以使用 useFormStatus 向用戶顯示即時反饋。

首先,建立一個在表單待處理時顯示載入狀態的元件:

'use client'
import { useFormStatus } from 'react-dom'

export default function SearchButton() {
  const status = useFormStatus()
  return (
    <button type="submit">{status.pending ? 'Searching...' : 'Search'}</button>
  )
}

然後,更新搜尋表單頁面以使用 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">Create Post</button>
    </Form>
  )
}

變更後,通常會重定向到新資源。您可以使用 next/navigation 中的 redirect 函數導航到新文章頁面。

須知:由於表單提交的「目的地」在動作執行前未知,<Form> 無法自動預取共享 UI。

'use server'
import { redirect } from 'next/navigation'

export async function createPost(formData: 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>
  )
}

更多範例請參閱伺服器動作 (Server Actions) 文檔。

On this page