Future APIs / useField

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

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

The field's default value as a string.

defaultOptions: string[] | undefined

Default selected options for multi-select fields or checkbox group.

defaultChecked: boolean | undefined

Default checked state for checkbox/radio inputs.

touched: boolean

Whether this field has been touched (through intent.validate() or the shouldValidate option).

invalid: boolean

Whether this field currently has validation errors.

errors: ErrorShape[] | undefined

Array of validation error messages for this field.

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.invalid ? '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-describedby={field.errors ? field.errorId : undefined}
32      />
33
34      {field.errors && (
35        <div id={field.errorId} className="field-errors">
36          {field.errors.map((error, i) => (
37            <p key={i} className="error-message">
38              {error}
39            </p>
40          ))}
41        </div>
42      )}
43    </div>
44  );
45}
46
47// Usage
48function MyForm() {
49  const { form, context } = useForm({
50    // ...
51  });
52
53  return (
54    <FormProvider context={form.context}>
55      <form {...form.props}>
56        <FormField name="firstName" label="First Name" />
57        <FormField name="email" label="Email" type="email" />
58        <FormField name="age" label="Age" type="number" />
59      </form>
60    </FormProvider>
61  );
62}

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 and htmlFor on the label
  • Use descriptionId for help text (aria-describedby)
  • Use errorId for error messages (aria-describedby)