Formulario
El componente <Form>
extiende el elemento HTML <form>
para proporcionar precarga (prefetching) de interfaz de carga (loading UI), navegación del lado del cliente (client-side navigation) al enviar, y mejora progresiva (progressive enhancement).
Es útil para formularios que actualizan parámetros de búsqueda en la URL, ya que reduce el código repetitivo necesario para lograr lo anterior.
Uso básico:
import Form from 'next/form'
export default function Page() {
return (
<Form action="/search">
{/* Al enviar, el valor del input se añadirá a
la URL, ej. /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">
{/* Al enviar, el valor del input se añadirá a
la URL, ej. /search?query=abc */}
<input name="query" />
<button type="submit">Submit</button>
</Form>
)
}
Referencia
El comportamiento del componente <Form>
depende de si la prop action
recibe un string
o una function
.
- Cuando
action
es un string, el<Form>
se comporta como un formulario HTML nativo que usa un métodoGET
. Los datos del formulario se codifican en la URL como parámetros de búsqueda, y cuando se envía el formulario, navega a la URL especificada. Además, Next.js:- Precarga (Prefetches) la ruta cuando el formulario se hace visible, esto precarga la UI compartida (ej.
layout.js
yloading.js
), resultando en una navegación más rápida. - Realiza una navegación del lado del cliente (client-side navigation) en lugar de una recarga completa de página al enviar el formulario. Esto mantiene la UI compartida y el estado del lado del cliente.
- Precarga (Prefetches) la ruta cuando el formulario se hace visible, esto precarga la UI compartida (ej.
- Cuando
action
es una función (Server Action),<Form>
se comporta como un formulario React, ejecutando la acción cuando se envía el formulario.
Props de action
(string)
Cuando action
es un string, el componente <Form>
soporta las siguientes props:
Prop | Ejemplo | Tipo | Requerido |
---|---|---|---|
action | action="/search" | string (URL o ruta relativa) | Sí |
replace | replace={false} | boolean | - |
scroll | scroll={true} | boolean | - |
prefetch | prefetch={true} | boolean | - |
action
: La URL o ruta a la que navegar cuando se envía el formulario.- Un string vacío
""
navegará a la misma ruta con los parámetros de búsqueda actualizados.
- Un string vacío
replace
: Reemplaza el estado actual del historial en lugar de añadir uno nuevo a la pila del historial del navegador. Por defecto esfalse
.scroll
: Controla el comportamiento de desplazamiento durante la navegación. Por defecto estrue
, lo que significa que se desplazará al inicio de la nueva ruta y mantendrá la posición de desplazamiento para navegación hacia atrás y adelante.prefetch
: Controla si la ruta debe ser precargada cuando el formulario se hace visible en el viewport del usuario. Por defecto estrue
.
Props de action
(función)
Cuando action
es una función, el componente <Form>
soporta la siguiente prop:
Prop | Ejemplo | Tipo | Requerido |
---|---|---|---|
action | action={myAction} | function (Server Action) | Sí |
action
: La Server Action que se llamará cuando se envíe el formulario. Consulte la documentación de React para más información.
Nota importante: Cuando
action
es una función, las propsreplace
yscroll
se ignoran.
Consideraciones
formAction
: Puede usarse en campos<button>
o<input type="submit">
para sobrescribir la propaction
. Next.js realizará una navegación del lado del cliente, sin embargo, este enfoque no soporta precarga.- Al usar
basePath
, también debe incluirse en la ruta deformAction
. Ej.formAction="/base-path/search"
.
- Al usar
key
: No se soporta pasar una propkey
a unaction
de tipo string. Si desea forzar un re-renderizado o realizar una mutación, considere usar unaction
de tipo función.
onSubmit
: Puede usarse para manejar lógica de envío de formularios. Sin embargo, llamar aevent.preventDefault()
sobrescribirá el comportamiento de<Form>
como navegar a la URL especificada.method
,encType
,target
: No están soportados ya que sobrescriben el comportamiento de<Form>
.- Similarmente,
formMethod
,formEncType
yformTarget
pueden usarse para sobrescribir las propsmethod
,encType
ytarget
respectivamente, y su uso hará que se recurra al comportamiento nativo del navegador. - Si necesita usar estas props, utilice el elemento HTML
<form>
en su lugar.
- Similarmente,
<input type="file">
: Usar este tipo de input cuandoaction
es un string coincidirá con el comportamiento del navegador al enviar el nombre del archivo en lugar del objeto de archivo.
Ejemplos
Formulario de búsqueda que lleva a una página de resultados
Puede crear un formulario de búsqueda que navegue a una página de resultados pasando la ruta como 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>
)
}
Cuando el usuario actualiza el campo de búsqueda y envía el formulario, los datos del formulario se codificarán en la URL como parámetros de búsqueda, ej. /search?query=abc
.
Nota importante: Si pasa un string vacío
""
aaction
, el formulario navegará a la misma ruta con los parámetros de búsqueda actualizados.
En la página de resultados, puede acceder a la consulta usando la prop searchParams
de page.js
y usarla para obtener datos de una fuente externa.
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>
}
Cuando el <Form>
se hace visible en el viewport del usuario, la UI compartida (como layout.js
y loading.js
) en la página /search
se precargará. Al enviar, el formulario navegará inmediatamente a la nueva ruta y mostrará la UI de carga mientras se obtienen los resultados. Puede diseñar la UI de respaldo usando loading.js
:
export default function Loading() {
return <div>Cargando...</div>
}
export default function Loading() {
return <div>Cargando...</div>
}
Para cubrir casos cuando la UI compartida aún no se ha cargado, puede mostrar retroalimentación instantánea al usuario usando useFormStatus
.
Primero, cree un componente que muestre un estado de carga cuando el formulario está pendiente:
Luego, actualice la página del formulario de búsqueda para usar el componente 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>
)
}
Mutaciones con Server Actions
Puede realizar mutaciones pasando una función a la prop 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">Crear Post</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">Crear Post</button>
</Form>
)
}
Después de una mutación, es común redirigir al nuevo recurso. Puede usar la función redirect
de next/navigation
para navegar a la página del nuevo post.
Nota importante: Como el "destino" del envío del formulario no se conoce hasta que se ejecuta la acción,
<Form>
no puede precargar automáticamente la UI compartida.
'use server'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
// Crear un nuevo post
// ...
// Redirigir al nuevo post
redirect(`/posts/${data.id}`)
}
'use server'
import { redirect } from 'next/navigation'
export async function createPost(formData) {
// Crear un nuevo post
// ...
// Redirigir al nuevo post
redirect(`/posts/${data.id}`)
}
Luego, en la nueva página, puede obtener datos usando la prop 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>
)
}
Consulte la documentación de Server Actions para más ejemplos.