rembrembdocs

Form Component

Last updated April 23, 2026

The <Form> component extends the HTML <form> element to provide prefetching of loading UI, client-side navigation on submission, and progressive enhancement.

It's useful for forms that update URL search params as it reduces the boilerplate code needed to achieve the above.

Basic usage:

/app/ui/search.tsx

JavaScriptTypeScript

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>
  )
}

Reference

The behavior of the <Form> component depends on whether the action prop is passed a string or function.

action (string) Props

When action is a string, the <Form> component supports the following props:

PropExampleTypeRequired
actionaction="/search"string (URL or relative path)Yes
replacereplace={false}boolean-
scrollscroll={true}boolean-
prefetchprefetch={true}boolean-

action (function) Props

When action is a function, the <Form> component supports the following prop:

PropExampleTypeRequired
actionaction={myAction}function (Server Action)Yes

Good to know: When action is a function, the replace and scroll props are ignored.

Caveats

Examples

Search form that leads to a search result page

You can create a search form that navigates to a search results page by passing the path as an action:

/app/page.tsx

JavaScriptTypeScript

import Form from 'next/form'
 
export default function Page() {
  return (
    <Form action="/search">
      <input name="query" />
      <button type="submit">Submit</button>
    </Form>
  )
}

When the user updates the query input field and submits the form, the form data will be encoded into the URL as search params, e.g. /search?query=abc.

Good to know: If you pass an empty string "" to action, the form will navigate to the same route with updated search params.

On the results page, you can access the query using the searchParams page.js prop and use it to fetch data from an external source.

/app/search/page.tsx

JavaScriptTypeScript

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>
}

When the <Form> becomes visible in the user's viewport, shared UI (such as layout.js and loading.js) on the /search page will be prefetched. On submission, the form will immediately navigate to the new route and show loading UI while the results are being fetched. You can design the fallback UI using loading.js:

/app/search/loading.tsx

JavaScriptTypeScript

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

To cover cases when shared UI hasn't yet loaded, you can show instant feedback to the user using useFormStatus.

First, create a component that displays a loading state when the form is pending:

/app/ui/search-button.tsx

JavaScriptTypeScript

'use client'
import { useFormStatus } from 'react-dom'
 
export default function SearchButton() {
  const status = useFormStatus()
  return (
    <button type="submit">{status.pending ? 'Searching...' : 'Search'}</button>
  )
}

Then, update the search form page to use the SearchButton component:

/app/page.tsx

JavaScriptTypeScript

import Form from 'next/form'
import { SearchButton } from '@/ui/search-button'
 
export default function Page() {
  return (
    <Form action="/search">
      <input name="query" />
      <SearchButton />
    </Form>
  )
}

Mutations with Server Actions

You can perform mutations by passing a function to the action prop.

/app/posts/create/page.tsx

JavaScriptTypeScript

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>
  )
}

After a mutation, it's common to redirect to the new resource. You can use the redirect function from next/navigation to navigate to the new post page.

Good to know: Since the "destination" of the form submission is not known until the action is executed, <Form> cannot automatically prefetch shared UI.

/app/posts/actions.ts

JavaScriptTypeScript

'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}`)
}

Then, in the new page, you can fetch data using the params prop:

/app/posts/[id]/page.tsx

JavaScriptTypeScript

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>
  )
}

See the Server Actions docs for more examples.

Was this helpful?