表單 (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>
)
}
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
是 字串 時,<Form>
的行為類似於使用GET
方法的原生 HTML 表單。表單資料會被編碼為 URL 搜尋參數,提交表單時會導航到指定 URL。此外,Next.js 還會:- 當表單可見時 預載 (prefetch) 路徑,這會預先載入共享 UI(例如
layout.js
和loading.js
),從而實現更快的導航。 - 提交表單時執行 客戶端導航 (client-side navigation) 而非完整頁面重新載入,這會保留共享 UI 和客戶端狀態。
- 當表單可見時 預載 (prefetch) 路徑,這會預先載入共享 UI(例如
- 當
action
是 函式(伺服器動作 Server Action)時,<Form>
的行為類似於 React 表單,在表單提交時執行該動作。
action
(字串) 屬性
當 action
是字串時,<Form>
元件支援以下屬性:
屬性 | 範例 | 類型 | 必填 |
---|---|---|---|
action | action="/search" | string (URL 或相對路徑) | 是 |
replace | replace={false} | boolean | - |
scroll | scroll={true} | boolean | - |
prefetch | prefetch={true} | boolean | - |
action
:提交表單時要導航到的 URL 或路徑。- 空字串
""
會導航到相同路由並更新搜尋參數。
- 空字串
replace
:替換當前歷史狀態,而不是在 瀏覽器歷史 堆疊中新增一項。預設為false
。scroll
:控制導航時的滾動行為。預設為true
,這意味著會滾動到新路由的頂部,並在向前和向後導航時保持滾動位置。prefetch
:控制當表單出現在使用者視口中時是否應預載路徑。預設為true
。
action
(函式) 屬性
當 action
是函式時,<Form>
元件支援以下屬性:
屬性 | 範例 | 類型 | 必填 |
---|---|---|---|
action | action={myAction} | function (Server Action) | 是 |
action
:提交表單時要呼叫的伺服器動作 (Server Action)。詳見 React 文檔。
須知:當
action
是函式時,replace
和scroll
屬性會被忽略。
注意事項
formAction
:可在<button>
或<input type="submit">
欄位中使用以覆寫action
屬性。Next.js 會執行客戶端導航,但此方法不支援預載。- 使用
basePath
時,必須在formAction
路徑中包含它,例如formAction="/base-path/search"
。
- 使用
key
:不支援將key
屬性傳遞給字串action
。如果需要觸發重新渲染或執行變異,請考慮改用函式action
。
onSubmit
:可用於處理表單提交邏輯。然而,呼叫event.preventDefault()
會覆寫<Form>
的行為,例如導航到指定 URL。method
、encType
、target
:不支援,因為它們會覆寫<Form>
的行為。- 類似地,
formMethod
、formEncType
和formTarget
可用於分別覆寫method
、encType
和target
屬性,使用它們會回退到原生瀏覽器行為。 - 如果需要使用這些屬性,請改用 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>
)
}
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>
}
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。您可以使用 loading.js
設計回退 UI:
export default function Loading() {
return <div>Loading...</div>
}
export default function Loading() {
return <div>Loading...</div>
}
為了涵蓋共享 UI 尚未載入的情況,您可以使用 useFormStatus
向使用者顯示即時反饋。
首先,建立一個在表單處於待處理狀態時顯示載入狀態的元件:
然後,更新搜尋表單頁面以使用 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">建立文章</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">建立文章</button>
</Form>
)
}
變異後,通常會重定向到新資源。您可以使用 next/navigation
中的 redirect
函式導航到新文章頁面。
須知:由於表單提交的「目的地」在動作執行前未知,
<Form>
無法自動預載共享 UI。
'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
屬性獲取資料:
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) 文檔。