useFormData
The
useFormDatahook is part of Conform's future export. These APIs are experimental and may change in minor versions. Learn more
A React hook that subscribes to live form values and derives state from them. Unlike useForm, it updates on every input change (but re-renders only when the result changes). Use it for dirty checking, computed values, or conditional UI.
import { useFormData } from '@conform-to/react/future';
const result = useFormData(formRef, selector, options);To detect form updates, the hook listens to:
- Form events:
input,focusout,submit, andreset - DOM mutations:
- When inputs are mounted or unmounted
- When the
name,form, ordata-conformattributes change
Manual changes to input values (e.g.
input.value = 'foo') are not tracked unless they also trigger an event or update thedata-conformattribute.
#Parameters
fromRef?: FormRef
A reference to the form to observe. You can pass either:
- A ref object from
useRef(), pointing to a form-associated element (e.g.<form>,<input>,<button>, etc.) - A string ID of a form element
selector?: (formData: FormData | URLSearchParams, lastResult?: Result) => Result
A function that derives a value from the current form data. It receives:
- The current form data, which may be:
- a
URLSearchParamsobject if theacceptFilesoption is not set orfalse - a
FormDataobject ifacceptFiles: true
- a
- The previously returned value (or undefined on first render)
The selector is only called when the form element is available. If the form is not available (e.g., on SSR or initial client render), the hook returns undefined without calling the selector.
The hook will re-run the selector whenever the form changes, and trigger a re-render only if the returned value is not deeply equal to the previous one.
options.acceptFiles?: boolean
Set to true to preserve file inputs and receive a FormData object in the selector. If omitted or false, the selector receives a URLSearchParams object, where all values are coerced to strings.
options.fallback?: Value
The fallback value to return when the form element is not available (e.g., on SSR or initial client render). If provided, the hook returns Value instead of Value | undefined.
#Returns
The value returned by your selector function. If the form element is not available:
- Returns
fallbackif provided - Returns
undefinedotherwise
#Example
Derive a single field value
1const name = useFormData(formRef, (formData) => formData.get('name') ?? '', {
2 fallback: '',
3});
4
5return <p>Hello, {name || 'guest'}!</p>;Compute a summary from multiple fields
1const total = useFormData(formRef, (formData) => {
2 const prices = ['itemA', 'itemB', 'itemC'];
3 return prices.reduce((sum, name) => {
4 const value = parseFloat(formData.get(name));
5 return sum + (isNaN(value) ? 0 : value);
6 }, 0);
7});
8
9return <p>Total: ${total?.toFixed(2) ?? 'n/a'}</p>;Conditionally show a section based on the form data
1const isSubscribed = useFormData(
2 formRef,
3 (formData) => formData.get('subscribe') === 'on',
4 { fallback: false },
5);
6
7return (
8 <>
9 <label>
10 <input type="checkbox" name="subscribe" />
11 Subscribe to newsletter
12 </label>
13
14 {isSubscribed && <NewsletterPreferences />}
15 </>
16);#Tips
You can use any form-related element as the reference
You don't need to pass a reference to the <form> element itself. The hook will resolve the associated form automatically, either by:
- the
formattribute (e.g.<button form="my-form">) - or by traversing up the DOM to find the closest
<form>ancestor
For example, here's how you might disable an Add to Cart button if the item is already selected in the form:
1function AddToCartButton({ itemId }: { itemId: string }) {
2 const buttonRef = useRef<HTMLButtonElement>(null);
3 const isAdded = useFormData(
4 buttonRef,
5 (formData) => formData.getAll('items').includes(itemId),
6 { fallback: false },
7 );
8
9 return (
10 <button ref={buttonRef} disabled={isAdded}>
11 Add to Cart
12 </button>
13 );
14}