Intent button
A submit button can 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. However, it also pollutes the form value with data that are used to control form behaviour.
1import { useForm } from '@conform-to/react';
2
3function Product() {
4 const [form] = useForm({
5 onSubmit(event, { submission }) {
6 event.preventDefault();
7
8 // This will log `{ productId: 'rf23g43', intent: 'add-to-cart' }`
9 // or `{ productId: 'rf23g43', intent: 'buy-now' }`
10 console.log(submission.payload);
11 },
12 });
13
14 return (
15 <form>
16 <input type="hidden" name="productId" value="rf23g43" />
17 <button type="submit" name="intent" value="add-to-cart">
18 Add to Cart
19 </button>
20 <button type="submit" name="intent" value="buy-now">
21 Buy now
22 </button>
23 </form>
24 );
25}
26
In Conform, if the name of a button is configured with conform.INTENT
, its value will be treated as the intent of the submission instead. Otherwise, the intent would be submit
by default.
1import { useForm, conform } from '@conform-to/react';
2
3function Product() {
4 const [form] = useForm({
5 onSubmit(event, { submission }) {
6 event.preventDefault();
7
8 // This will log `add-to-cart` or `buy-now`
9 console.log(submission.intent);
10
11 // This will log `{ productId: 'rf23g43' }`
12 console.log(submission.payload);
13 },
14 });
15
16 return (
17 <form {...form.props}>
18 <input type="hidden" name="productId" value="rf23g43" />
19 <button type="submit" name={conform.INTENT} value="add-to-cart">
20 Add to Cart
21 </button>
22 <button type="submit" name={conform.INTENT} value="buy-now">
23 Buy now
24 </button>
25 </form>
26 );
27}
28
List intent
Conform provides built-in list intent button builder for you to modify a list of fields.
1import { useForm, useFieldList, conform, list } from '@conform-to/react';
2
3export default function Todos() {
4 const [form, { tasks }] = useForm();
5 const taskList = useFieldList(form.ref, tasks);
6
7 return (
8 <form {...form.props}>
9 <ul>
10 {taskList.map((task, index) => (
11 <li key={task.key}>
12 <input name={task.name} />
13 <button {...list.remove(tasks.name, { index })}>Delete</button>
14 </li>
15 ))}
16 </ul>
17 <div>
18 <button {...list.insert(tasks.name)}>Add task</button>
19 </div>
20 <button>Save</button>
21 </form>
22 );
23}
24
Validate intent
A validation can be triggered by configuring a button with the validate intent.
1import { useForm, validate } from '@conform-to/react';
2
3export default function Todos() {
4 const [form, { email }] = useForm();
5
6 return (
7 <form {...form.props}>
8 <input name={email.name} />
9 {/* Validating field manually */}
10 <button {...validate(email.name)}>Validate email</button>
11 <button>Send</button>
12 </form>
13 );
14}
15
Triggering intent
Sometimes, it could be useful to trigger an intent without requiring users to click on the intent button. We can achieve it by capturing the button element with useRef
and triggering the intent with button.click()
1import { useForm, useFieldList, conform, list } from '@conform-to/react';
2import { useEffect } from 'react';
3
4export default function Todos() {
5 const [form, { tasks }] = useForm();
6 const buttonRef = useRef<HTMLButtonElement>(null);
7 const taskList = useFieldList(form.ref, tasks);
8
9 useEffect(() => {
10 if (taskList.length === 0) {
11 // Ensure at least 1 task is shown
12 // by clicking on the Add task button
13 buttonRef.current?.click();
14 }
15 }, [taskList.length]);
16
17 return (
18 <form {...form.props}>
19 <ul>
20 {taskList.map((task, index) => (
21 <li key={task.key}>
22 <input name={task.name} />
23 </li>
24 ))}
25 </ul>
26 <div>
27 <button {...list.insert(tasks.name)} ref={buttonRef}>
28 Add task
29 </button>
30 </div>
31 <button>Save</button>
32 </form>
33 );
34}
35
However, if the intent button can not be pre-configured easily, like drag and drop an item on the list with dynamic from
/ to
index, an intent can be triggered by using the requestIntent helper.
1import {
2 useForm,
3 useFieldList,
4 conform,
5 list,
6 requestIntent,
7} from '@conform-to/react';
8import DragAndDrop from 'awesome-dnd-example';
9
10export default function Todos() {
11 const [form, { tasks }] = useForm();
12 const taskList = useFieldList(form.ref, tasks);
13
14 const handleDrop = (from, to) =>
15 requestIntent(form.ref.current, list.reorder({ from, to }));
16
17 return (
18 <form {...form.props}>
19 <DragAndDrop onDrop={handleDrop}>
20 {taskList.map((task, index) => (
21 <div key={task.key}>
22 <input name={task.name} />
23 </div>
24 ))}
25 </DragAndDrop>
26 <button>Save</button>
27 </form>
28 );
29}
30
Conform is also utilizing the requestIntent
helper to trigger the validate
intent on input or blur event.