Future APIs / BaseControl

BaseControl

The BaseControl component is part of Conform's future export. These APIs are experimental and may change in minor versions. Learn more

A React component that renders hidden native form controls. It could be used with useControl to bridge custom UI components back to standard form submission.

1import { BaseControl, useControl } from '@conform-to/react/future';
2
3const control = useControl({
4  defaultValue: 'initial value',
5});
6
7<BaseControl
8  name="title"
9  ref={control.register}
10  defaultValue={control.defaultValue}
11/>;

#Props

name: string

Name used for the rendered hidden control(s).

defaultValue: unknown

Default value used to render the hidden control(s).

type?: 'text' | 'file' | 'fieldset' | 'select' | 'textarea' | ...

Controls which hidden element is rendered:

  • fieldset: renders a hidden <fieldset> and recursively renders nested hidden <input> elements from defaultValue
  • select: renders a hidden <select>
  • textarea: renders a hidden <textarea>
  • otherwise: renders a hidden <input type={type}>

form?: string

Form id to associate the hidden control(s) with when rendered outside the <form> element.

#Examples

Hidden text input for simple values

1const control = useControl({
2  defaultValue: field.defaultValue,
3});
4
5return (
6  <>
7    <BaseControl
8      name={field.name}
9      ref={control.register}
10      defaultValue={control.defaultValue}
11    />
12    <CustomTextInput
13      value={control.value ?? ''}
14      onChange={(value) => control.change(value)}
15      onBlur={() => control.blur()}
16    />
17  </>
18);

Hidden fieldset for structured values

For type="fieldset", object and array shapes are expanded into nested names like range.start and members[0].id.

control.defaultValue drives the hidden inputs that are rendered, while control.payload is the current logical value exposed to your custom component.

1const control = useControl({
2  defaultValue: field.defaultPayload,
3  parse(payload) {
4    return DateRangeSchema.parse(payload);
5  },
6});
7
8return (
9  <>
10    <BaseControl
11      type="fieldset"
12      name={field.name}
13      ref={control.register}
14      defaultValue={control.defaultValue}
15    />
16    {/*
17      This renders a hidden fieldset with nested inputs:
18      
19      <fieldset name="range" hidden>
20        <input name="range.start" value={control.defaultValue.start} />
21        <input name="range.end" value={control.defaultValue.end} />
22      </fieldset>
23
24    */}
25    <CustomDateRangePicker
26      value={control.payload}
27      onChange={(value) => control.change(value)}
28      onBlur={() => control.blur()}
29    />
30  </>
31);