Form
Within a Hydrogen app, React Server Components are used to fetch data and API routes are used to mutate data. The Form
component provides a declarative way to send data to API routes and re-render server components. The component mimics the functionality of a native <form>
element, but it provides an enhanced user experience with client-side JavaScript.
HTML form
element
Anchor link to section titled "HTML form element"The Form
component builds on the native HTML <form>
element. The following is an example:
This example HTML doesn't run any JavaScript. When Submit is clicked, the browser sends a POST
request to /login
with each form field encoded. The browser also reloads the entire page to display the server's response. Learn more about native HTML forms.
Hydrogen Form
component
Anchor link to section titled "Hydrogen Form component"Hydrogen's Form
component mimics the functionality of a native <form>
element, while providing an enhanced experience with client-side JavaScript.
Native HTML forms work without JavaScript. However, JavaScript can provide the following improvements:
- Performance: JavaScript prevents the entire page from reloading to display responses from the server.
- UX: JavaScript provides client-side validation and feedback. Client-side validation is quicker than making a round trip to the server, and feedback helps the user know when the form is in the process of submitting.
Client validation and feedback
Anchor link to section titled "Client validation and feedback"The best user experience has client-side validation and gives user feedback while the form is submitting. This requires a client component.
Example code
Anchor link to section titled "Example code"The following example rewrites the example form element by substituting the native HTML with a Form
component that's imported from Hydrogen.
Because the Form
is within a client component, children
can be a render prop. This enables you to give users feedback while the form is submitting.
Hidden fields
Anchor link to section titled "Hidden fields"You can use the Form
component for any mutation that doesn't include a text field.
For example, the following uses a Form
component for adding items an item to a cart:
The hidden input field for the productId
is sent to the server when the Add to cart button is clicked. The API route at /addToCart
can contain all the logic to add the product to the cart and re-render the page. The button is actionable before the page fully loads and the JavaScript is hydrated.
Form in server components
Anchor link to section titled "Form in server components"We recommend that you use Form
in client components for the best user experience. However, you can use Form
in server components if required. The following is an example:
Form
requires an API route
Anchor link to section titled "Form requires an API route"The action
attribute must point to an API route. The following is an example implementation:
Read data in the API route from the Form
by using the FormData
API. The API route must respond with a new Request()
. This renders the server components for the given page. You can re-render the current page, or render an entirely different page in the app.
In the previous example, when the user is not found, the current page is re-rendered with an error set on the session. The following code updates the server component to render the login error:
The page initially loads without a session loginError
value. If the login mutation fails, then the server components re-render with the loginError
session value and display a message to the user. Because the component uses useFlashSession
instead of useSession
, the value is subsequently cleared. If the user refreshes the page, then the validation error goes away.
The Form
component shares the same props that are available to the native <form>
element with the following additions:
Name | Type | Description |
---|---|---|
action | string |
The path to the API route that the form submits to. The API route must respond with a new Request() . |
encType? | string |
The MIME type of the form submission. multipart/form-data is not yet supported. |
onSubmit? | Function |
A callback to intercept a submission event. The form doesn't submit if event.preventDefault() is called. |
children? | ReactNode or ({loading, error}) => ReactNode |
Either pass any ReactNode, or a function that returns a ReactNode. That function will receive loading and error parameters. loading is true while forms are being submitted. error is populated when there's an error communicating with the server. |
Component type
Anchor link to section titled "Component type"The Form
component is a client component, so it renders on the client. For more information about component types, refer to React Server Components.