Guides / Intent button

Intent button

A submit button will contribute to the form data when it triggers the submission as a submitter.

#Submission Intent

The submitter is particularly useful when you want to extend the form with different behaviour based on the intent.

1import { useForm } from '@conform-to/react';
2
3function Product() {
4  const [form] = useForm({
5    onSubmit(event, { formData }) {
6      event.preventDefault();
7
8      switch (formData.get('intent')) {
9        case 'add-to-cart':
10          // Add to cart
11          break;
12        case 'buy-now':
13          // Buy now
14          break;
15      }
16    },
17  });
18
19  return (
20    <form id={form.id}>
21      <input type="hidden" name="productId" value="rf23g43" />
22      <button type="submit" name="intent" value="add-to-cart">
23        Add to Cart
24      </button>
25      <button type="submit" name="intent" value="buy-now">
26        Buy now
27      </button>
28    </form>
29  );
30}

#Form Controls

Conform utilizes the submission intent for all form controls, such as validating or removing a field. This is achieved by giving the buttons a reserved name with the intent serialized as the value. To simplify the setup, Conform provides a set of form control helpers, such as form.validate, form.reset or form.insert.

Validate intent

To trigger a validation, you can configure a button with the validate intent.

1import { useForm } from '@conform-to/react';
2
3function EmailForm() {
4  const [form, fields] = useForm();
5
6  return (
7    <form id={form.id}>
8      <input name={fields.email.name} />
9      <button {...form.validate.getButtonProps({ name: fields.email.name })}>
10        Validate Email
11      </button>
12    </form>
13  );
14}

When the button is clicked, conform identifies the serialized intent with the reserved name and trigger a validation by marking the email field as validated and returns the error message if the email is invalid.

However, if you want to trigger the validation once the user leaves the field, you can also trigger the validate intent directly.

1import { useForm } from '@conform-to/react';
2
3function EmailForm() {
4  const [form, fields] = useForm();
5
6  return (
7    <form id={form.id}>
8      <input
9        name={fields.email.name}
10        onBlur={(event) => form.validate({ name: event.target.name })}
11      />
12    </form>
13  );
14}

Reset and Update intent

You can also modify a field with the reset and update intent.

1import { useForm } from '@conform-to/react';
2
3export default function Tasks() {
4  const [form, fields] = useForm();
5
6  return (
7    <form id={form.id}>
8      <button {...form.reset.getButtonProps()}>Reset form</button>
9      <button
10        {...form.reset.getButtonProps({
11          name: fields.tasks.name,
12        })}
13      >
14        Reset field (including nested / list field)
15      </button>
16      <button
17        {...form.update.getButtonProps({
18          name: fields.agenda.name,
19          value: {
20            title: 'My agenda',
21            description: 'This is my agenda',
22          },
23        })}
24      >
25        Update field (including nested / list field)
26      </button>
27      <button
28        {...form.update.getButtonProps({
29          validated: false,
30        })}
31      >
32        Clear all error
33      </button>
34    </form>
35  );
36}

Be aware that both intents requires setting up the inputs with the key from the field metadata. As Conform relies on the key to notify React for re-mounting the input with the updated initialValue. The only exception is if you are setting up a controlled input with the useInputControl hook which will reset the value when the key changes.

Insert, remove and reorder intents

To manipulate a field list, you can use the insert, remove and reorder intents.

1import { useForm } from '@conform-to/react';
2import { parseWithZod } from "@conform-to/zod";
3import { z } from "zod";
4
5const todosSchema = z.object({
6  title: z.string(),
7  tasks: z.array(z.string()),
8});
9
10export default function Tasks() {
11  const [form, fields] = useForm({
12    onValidate({ formData }) {
13      return parseWithZod(formData, { schema: todosSchema });
14    },
15    shouldValidate: "onBlur",
16  });
17  const tasks = fields.tasks.getFieldList();
18
19  return (
20    <form id={form.id} onSubmit={form.onSubmit}>
21      <ul>
22        {tasks.map((task, index) => (
23          <li key={task.key}>
24            <input name={task.name} />
25            <button
26              {...form.reorder.getButtonProps({
27                name: fields.tasks.name,
28                from: index,
29                to: 0,
30              })}
31            >
32              Move to top
33            </button>
34            <button
35              {...form.remove.getButtonProps({
36                name: fields.tasks.name,
37                index,
38              })}
39            >
40              Delete
41            </button>
42          </li>
43        ))}
44      </ul>
45      <button
46        {...form.insert.getButtonProps({
47          name: fields.tasks.name,
48        })}
49      >
50        Add task
51      </button>
52      <button>Save</button>
53    </form>
54  );
55}