Forms

Forms and Progressive Enhancement

Write <form action={createNote}> in a Server Component and React 7,897 submits the fields to that action as FormData (Form Actions). Turning JavaScript off changes nothing: the capture below came from Chrome 1 with scripting disabled, new note and success message included.

The same form submitted with JavaScript disabled in Chrome
The same form submitted with JavaScript disabled in Chrome

Next.js 10,514 manages that by rendering a real HTML form whose hidden fields encode which action to run and with what state:

What the server actually rendersHTMLLive
<form action="" encType="multipart/form-data" method="POST">
  <input type="hidden" name="$ACTION_REF_1"/>
  <input type="hidden" name="$ACTION_1:0" value='{"id":"6064...e653","bound":"$@1"}'/>
  <input type="hidden" name="$ACTION_1:1" value='[{"error":null}]'/>
  <input type="hidden" name="$ACTION_KEY" value="k2b744d11..."/>   <!-- then your fields -->

Without JavaScript the browser posts that multipart body to the current URL, Next.js recognizes the $ACTION_ fields, runs the action and returns a full page. With JavaScript, React intercepts the submit and sends the compact Next-Action request instead, so nothing reloads. In between, before hydration finishes, submissions are queued and the form is prioritized for hydration, so an early click is not lost. This works only for a form a Server Component rendered; a 'use client' form has no reference to fall back on.

A FormData carries only what the form contains, so an action needing a record ID uses toggleNote.bind(null, note.id, true). bind prepends its arguments before the FormData, keeps progressive enhancement, and is encrypted with the reference, whereas a hidden input sits in the page source as plain text. Neither value is trustworthy, so the action still checks ownership.