Form

The Form component groups and manages related form fields together. The Form manages the state of its fields, making it a convenient way to submit user input without having to write individual event handlers for each input.

Basic usage

Try entering values in the form below and clicking "Submit". A map of the form field IDs to their values passed to the onSubmit handler.
tsx
Copied
1
<Form
2
onSubmit={(values) => {
3
alert(`Submitted with values: ${JSON.stringify(values)}`);
4
}}
5
>
6
<TextInput id="name" label="Cat name" />
7
<Select
8
id="breed"
9
label="Breed"
10
data={["American Wirehair", "British Shorthair", "Taby"]}
11
/>
12
</Form>

Task-backed forms

A common use for Form is to execute a task when the form is submitted. If the slug of an existing task or an AirplaneFunc (output of airplane.task) is passed to the form via the task prop, the form will automatically populate itself with the appropriate fields based on the task parameters. Then, when the form is submitted, the form executes the associated task.
Forms can also execute runbooks when a runbook is passed via the runbook prop.
tsx
Copied
1
return <Form task="pet_cat" />;

Accessing autogenerated inputs on the component state

The inputs automatically created by a task backed form have a consistent id, formed by joining the form id and the slug of the task parameter with a . delimiter. This id can be used to manipulate the input values using the component state.
tsx
Copied
1
const { id, values } = useComponentState("formid");
2
const { value, setValue } = useComponentState("formid.num_times");
3
return (
4
<Stack>
5
<Form id={id} task="pet_cat" />
6
<Button onClick={() => setValue(value ? value + 1 : 1)}>More pets</Button>
7
<Text>{JSON.stringify(values)}</Text>
8
<Text>{value}</Text>
9
</Stack>
10
);
Even though the input values are prepended with the form id, the values received by all Form callbacks, such as beforeSubmitTransform and onSubmit, will be keyed according to the original parameter slugs.

Customizing fields

The fields of a task- or runbook-backed form can be customized using the field slugs. This can be helpful when the associated task or runbook has a lot of parameters, or if the values of some parameters are known ahead of time.
  • If set, only fields in shownFields will be displayed.
  • Fields in hiddenFields will be hidden.
  • fieldOptions can be used to constrain the values of a field, set a default value, or set a validation function.
tsx
Copied
1
return (
2
<Form
3
task={{
4
slug: "pet_cat",
5
hiddenFields: ["num_times"],
6
fieldOptions: [
7
{ slug: "num_times", value: 1 },
8
{ slug: "name", defaultValue: "Bootz", allowedValues: ["Bootz", "Hazel", "Baosky"] },
9
],
10
}}
11
/>
12
);
Note that if a parameter is required, but not displayed in the form, its value must be set in one of the field options.

Calling a task with useTaskMutation

The standard task-backed form should be used whenever a task should be run on form submit, using the form values. If direct control over task execution or transformations on the form values is needed, useTaskMutation can be used in conjunction with a standard form.
tsx
Copied
1
const { id, values } = useComponentState();
2
const { output, mutate } = useTaskMutation({ slug: "search_animals", params: values });
3
return (
4
<Stack>
5
<Form
6
id={id}
7
onSubmit={() => {
8
mutate();
9
}}
10
>
11
<Select id="animal" required label="Animal" data={[{ value: "cat", label: "Cat" }]} />
12
</Form>
13
{output && <Table data={output} />}
14
</Stack>
15
);

Validation

Required inputs

Form can be used to validate user input. All form fields take the required prop, which indicates that the input must be filled out before the form can be successfully submitted.
tsx
Copied
1
<Form
2
onSubmit={(values) => {
3
alert(`Submitted with values: ${JSON.stringify(values)}`);
4
}}
5
>
6
<TextInput required id="name" label="Cat name" />
7
<Select
8
required
9
id="breed"
10
label="Breed"
11
data={["American Wirehair", "British Shorthair", "Taby"]}
12
/>
13
</Form>

Custom validation

All form fields support custom validation functions. The validate prop on an input takes a function or list of functions, each of which takes in the input's value. If the function(s) return a string, the input is considered invalid. To add validation to a task-backed form, set the fieldOptions.
Form will automatically validate its fields on submit.
tsx
Copied
1
<Form
2
onSubmit={(values) => {
3
alert(`Submitted with values: ${JSON.stringify(values)}`);
4
}}
5
>
6
<TextInput
7
id="name"
8
label="Cat name"
9
defaultValue="Spot"
10
validate={[validateLength, validateNoSpot]}
11
/>
12
<Select
13
required
14
id="breed"
15
label="Breed"
16
defaultValue="Taby"
17
data={["American Wirehair", "British Shorthair", "Taby"]}
18
validate={validateNoTaby}
19
/>
20
</Form>

Dependent fields validation

You have access to all of a form's values by tapping into the component state for the form. This way, you can set up custom validation for dependent form fields.
tsx
Copied
1
const { id, values } = useComponentState();
2
return (
3
<Stack>
4
<Form
5
id={id}
6
onSubmit={(values) => {
7
alert(`Submitted with values: ${JSON.stringify(values)}`);
8
}}
9
>
10
<TextInput id="name" label="Cat name" defaultValue="Sir Reginald" />
11
<Select
12
required
13
id="breed"
14
label="Breed"
15
defaultValue="American Wirehair"
16
data={["American Wirehair", "British Shorthair", "Taby"]}
17
validate={(value) => {
18
if (values?.name && values?.name.startsWith("Sir") && value !== "British Shorthair") {
19
return "Only British Shorthairs are allowed to be named Sir";
20
}
21
}}
22
/>
23
</Form>
24
</Stack>
25
);

Resetting form field values

By default, the form will reset its field values when the form is submitted. You can disable this behavior by setting the resetOnSubmit prop to false.
tsx
Copied
1
<Form
2
resetOnSubmit={false}
3
onSubmit={(values) => {
4
alert(`Submitted with values: ${JSON.stringify(values)}`);
5
}}
6
>
7
<TextInput id="name" label="Cat name" />
8
<Select
9
id="breed"
10
label="Breed"
11
// Resetting the form will reset the selected value to the defaultValue.
12
// In this case, the value after reset will be "Taby"
13
defaultValue="Taby"
14
data={["American Wirehair", "British Shorthair", "Taby"]}
15
/>
16
</Form>
We also provide a reset method on the form's state API that you can call to programmatically reset form field values.
tsx
Copied
1
const { id, reset } = useComponentState();
2
return (
3
<Stack>
4
<Form
5
id={id}
6
submitText="Reset"
7
onSubmit={() => {
8
reset();
9
}}
10
>
11
<TextInput id="name" label="Cat name" />
12
<Select
13
id="breed"
14
label="Breed"
15
defaultValue="Taby"
16
data={["American Wirehair", "British Shorthair", "Taby"]}
17
/>
18
</Form>
19
</Stack>
20
);

Supported input components

Component API

beforeSubmitTransform
optional
(values: State) => State

Function applied to transform the form's input state before being passed to onSubmit. This will affect the parameters received by the task for a task-backed form.

children
optional
React.ReactNode

The form children.

disabled
optional
boolean

If true, the submit button is always disabled. The submit button may disabled even if this is set to false, for example if the form has errors or if the user does not have permission to submit a task-backed form.

grow
optional
boolean

If true, the element will grow to fill available space.

This prop works only if the element is a direct child of a Stack.

height
optional
default: auto
SizingToken | "{number}px"
Defines the height of the component. See SizingToken docs for more details.
id
optional
string

The ID referenced by the global component state.

onSubmit
optional
(state: State) => void

Callback for when the form is submitted. The callback is passed the form's input state as input.

resetOnSubmit
optional
default: true
boolean

If true, the form input state will be reset to their initial values on submit.

runbook
optional
string | { slug: string; shownFields?: string[]; hiddenFields?: string[]; fieldOptions?: { slug: string; value?: string | number | boolean | Date; defaultValue?: string | number | Date; allowedValues?: string[] | number[] | Date[]; validate?: (value: any) => string | undefined; }[]; }
The runbook associated with the form. This can either be a string, corresponding to the runbook slug, or an options struct, for which the runbook slug must be passed in the slug field.
If shownFields is set, only fields in shownFields are shown.
If hiddenFields is set, fields in hiddenFields are hidden.
fieldOptions can be used to configure the values for a field. Each option in fieldOptions has a slug field, corresponding to the parameter slug.
submitText
optional
default: Submit
string

Label on the form's submit button.

submitting
optional
boolean

If true, the submit button is disabled and a spinner is shown. This is set automatically when using a task-backed form.

task
optional
string | AirplaneFunc | { slug?: string; fn?: AirplaneFunc; shownFields?: string[]; hiddenFields?: string[]; fieldOptions?: { slug: string; value?: string | number | boolean | Date; defaultValue?: string | number | Date; allowedValues?: string[] | number[] | Date[]; validate?: (value: any) => string | undefined; }[]; refetchTasks?: RefetchQuery | RefetchQuery[]; onSuccess?: (output: TOutput, runID: string) => void; onError?: ( output: TOutput | undefined, error: ExecuteError, runID?: string ) => void; }
The task associated with the form. This can either be a string, corresponding to the task slug, or an options struct, for which the task slug must be passed in the slug field.
If shownFields is set, only fields in shownFields are shown.
If hiddenFields is set, fields in hiddenFields are hidden.
fieldOptions can be used to configure the values for a field. Each option in fieldOptions has a slug field, corresponding to the parameter slug. One of fn or slug is required.
The onSuccess and onError callbacks will run on task success or failure.
If you want to trigger a refetch of some other task(s) when this task completes, set the refetchTasks param. See refetchTasks on a mutation for more information.
width
optional
default: auto
SizingToken | "{number}px"
Defines the width of the component. See SizingToken docs for more details.

State API

reset
() => void

Function to reset all of the form's inputs

values
Record<string, any>

Map of the form's input IDs to their values