useField
The
useField
function is part of Conform's future export. These APIs are experimental and may change in minor versions. Learn more
A React hook that provides access to a specific field's metadata and state from any component within a FormProvider
context.
import { useField } from '@conform-to/react/future';
const field = useField(name, options);
#Parameters
name: FieldName<FieldShape>
The name of the input field.
options.formId?: string
Optional form identifier to target a specific form when multiple forms are rendered. If not provided, uses the nearest form context.
#Returns
A Field
object containing field metadata and state:
id: string
The field's unique identifier, automatically generated as {formId}-field-{fieldName}
.
name: FieldName<FieldShape>
The field name path exactly as provided. The FieldName type is just a branded string type to help with TypeScript inference.
formId: string
The form's unique identifier for associating field via the form
attribute.
descriptionId: string
Auto-generated ID for associating field descriptions via aria-describedby
.
errorId: string
Auto-generated ID for associating field errors via aria-describedby
.
defaultValue: string
The field's default value as a string. Returns an empty string ''
when:
- No default value is set (field value is
null
orundefined
) - The field value cannot be serialized to a string (e.g., objects or arrays)
defaultOptions: string[]
Default selected options for multi-select fields or checkbox group. Returns an empty array []
when:
- No default options are set (field value is
null
orundefined
) - The field value cannot be serialized to a string array (e.g., nested objects or arrays of objects)
defaultChecked: boolean
Default checked state for checkbox inputs. Returns true
if the field value is 'on'
. For radio buttons, compare the field's defaultValue
with the radio button's value attribute instead.
touched: boolean
Whether this field has been touched (through intent.validate()
or the shouldValidate
option).
valid: boolean
Whether this field currently has no validation errors.
invalid: boolean
⚠️ Deprecated: Use
valid
instead. This property will be removed in version 1.11.0.
Whether this field currently has validation errors. This is equivalent to !valid
.
errors: ErrorShape[] | undefined
Array of validation error messages for this field.
fieldErrors: Record<string, ErrorShape[]>
Object containing errors for all touched subfields.
ariaInvalid: boolean | undefined
Boolean value for the aria-invalid
attribute. Indicates whether the field has validation errors for screen readers. This is true
when the field has errors, undefined
otherwise.
ariaDescribedBy: string | undefined
String value for the aria-describedby
attribute. Contains the errorId
when the field is invalid, undefined
otherwise. If you need to reference both help text and errors, merge with descriptionId
manually (e.g., ${field.descriptionId} ${field.ariaDescribedBy}
).
Validation Attributes
HTML validation attributes automatically derived from schema constraints:
required?: boolean
minLength?: number
maxLength?: number
pattern?: string
min?: string | number
max?: string | number
step?: string | number
multiple?: boolean
getFieldset(): Fieldset<FieldShape>
Method to get nested fieldset for object fields under this field.
getFieldList(): Field[]
Method to get array of fields for list/array fields under this field.
#Example
Dynamic field components
1import { useField } from '@conform-to/react/future';
2
3interface FieldProps {
4 name: string;
5 label: string;
6 type?: string;
7}
8
9function FormField({ name, label, type = 'text' }: FieldProps) {
10 const field = useField(name);
11
12 return (
13 <div className={`form-field ${field.valid ? 'valid' : 'invalid'}`}>
14 <label htmlFor={field.id}>
15 {label}
16 {field.required && <span className="required">*</span>}
17 </label>
18
19 <input
20 id={field.id}
21 name={field.name}
22 type={type}
23 defaultValue={field.defaultValue}
24 required={field.required}
25 minLength={field.minLength}
26 maxLength={field.maxLength}
27 pattern={field.pattern}
28 min={field.min}
29 max={field.max}
30 step={field.step}
31 aria-invalid={field.ariaInvalid}
32 aria-describedby={field.ariaDescribedBy}
33 />
34
35 {field.errors && (
36 <div id={field.errorId} className="field-errors">
37 {field.errors.map((error, i) => (
38 <p key={i} className="error-message">
39 {error}
40 </p>
41 ))}
42 </div>
43 )}
44 </div>
45 );
46}
47
48// Usage
49function MyForm() {
50 const { form, context } = useForm({
51 // ...
52 });
53
54 return (
55 <FormProvider context={form.context}>
56 <form {...form.props}>
57 <FormField name="firstName" label="First Name" />
58 <FormField name="email" label="Email" type="email" />
59 <FormField name="age" label="Age" type="number" />
60 </form>
61 </FormProvider>
62 );
63}
Nested field access
1import { useField } from '@conform-to/react/future';
2
3function AddressFields({ name }: { name: string }) {
4 const field = useField(name);
5 const address = field.getFieldset();
6
7 return (
8 <fieldset>
9 <legend>Address</legend>
10
11 <div>
12 <label htmlFor={address.street.id}>Street Address</label>
13 <input
14 id={address.street.id}
15 name={address.street.name}
16 defaultValue={address.street.defaultValue}
17 required={address.street.required}
18 />
19 {address.street.errors && (
20 <div className="error">{address.street.errors.join(', ')}</div>
21 )}
22 </div>
23
24 <div>
25 <label htmlFor={address.city.id}>City</label>
26 <input
27 id={address.city.id}
28 name={address.city.name}
29 defaultValue={address.city.defaultValue}
30 required={address.city.required}
31 />
32 {address.city.errors && (
33 <div className="error">{address.city.errors.join(', ')}</div>
34 )}
35 </div>
36
37 <div>
38 <label htmlFor={address.zip.id}>ZIP Code</label>
39 <input
40 id={address.zip.id}
41 name={address.zip.name}
42 defaultValue={address.zip.defaultValue}
43 pattern={address.zip.pattern}
44 required={address.zip.required}
45 />
46 {address.zip.errors && (
47 <div className="error">{address.zip.errors.join(', ')}</div>
48 )}
49 </div>
50 </fieldset>
51 );
52}
Array field handling
1import { useField } from '@conform-to/react/future';
2
3function TagInput({ name }: { name: string }) {
4 const field = useField(name);
5 const tagFields = field.getFieldList();
6
7 return (
8 <div>
9 <label>Tags</label>
10 {tagFields.map((tagField) => (
11 <div key={tagField.key}>
12 <input name={tagField.name} defaultValue={tagField.defaultValue} />
13 {tagField.errors && (
14 <span className="error">{tagField.errors.join(', ')}</span>
15 )}
16 </div>
17 ))}
18 </div>
19 );
20}
#Tips
Accessibility
The hook provides auto-generated IDs for proper ARIA associations:
- Use
id
for the input element andhtmlFor
on the label - Use
descriptionId
for help text (aria-describedby
) - Use
errorId
for error messages (aria-describedby
)