# Lend a form's fields

> Let the assistant fill a page's inputs, selects and switches with AssistedInput, or any control with AssistedField.

An assisted field is a field on an assisted page the assistant fills as the user would type into it. Each field is a tool of its name, and the page's note shows what each field says now. Render it inside an `AssistedPage`. Outside one, it throws.

A fill changes the form, not your data. Keep saving on the page's own button, lent as an [action](https://superassist.app/how-to/add-actions.md).

## Wrap a field

Wrap the input, textarea, native select, checkbox or switch in `AssistedInput` with a `name` and a `label`. The label says what the field holds. From `examples/app/app/orders/[id]/page.tsx`:

```tsx
<AssistedInput name="note" label="The order's note">
  <Textarea value={draft} onChange={(event) => setDraft(event.target.value)} />
</AssistedInput>
```

`AssistedInput` takes one child and attaches a ref to it. The child must pass that ref to the field's DOM element. shadcn's `Input`, `Textarea` and `NativeSelect` do.

A fill writes through the element's own value setter and fires an `input` event, so your `onChange` runs as if the user typed. Uncontrolled forms see the new value too. Once the page has rendered, the fill answers `Filled "<label>"; it now says <value>.`

## Group a form's fields by id

Give every field the same `name` and its own `id`. The assistant sees one tool that fills a field by id. From `examples/app/app/settings/page.tsx`:

<!-- prettier-ignore -->
```tsx
{fields.map(([field, id, label]) => (
  <Label key={field} className="grid gap-1.5">
    {label}
    <AssistedInput name="setting" id={id} label={label}>
      <Input value={draft[field]} onChange={(event) => set(field, event.target.value)} />
    </AssistedInput>
  </Label>
))}
```

The page's note then lists the group with each field's value:

```md
Its fields: setting (name: Shop name, now "Northwind"; currency: Currency, now "USD"; email: Notification email, now "alerts@northwind.example").
```

Say in the page's `instructions` which action saves the form, so the assistant fills and then saves.

## Know what each field takes

`AssistedInput` reads the wrapped element when it mounts and fills it by its kind:

| Child                        | A fill                                                  | The note shows                  |
| ---------------------------- | ------------------------------------------------------- | ------------------------------- |
| `<input>`, `<textarea>`      | Sets the value, fires `input`                           | The text, cut at 200 characters |
| `<input type="password">`    | Sets the value, fires `input`                           | `(filled)` or `(empty)`         |
| Checkbox, or `role="switch"` | Clicks it when it is not already `on` or `off` as asked | `on` or `off`                   |
| `<select>`                   | Sets the value, fires `change`                          | The selected value              |
| Anything else                | Rings it for the user                                   | Its text                        |

A select's options become the only values its tool takes, read from the DOM when the field mounts. A switch or checkbox takes `on` and `off`.

## Leave a field to the user

Pass `consequential` when the user should type the value themselves. A call then rings the field and answers that filling it is theirs:

```tsx
<AssistedInput name="email" label="Your email" consequential>
  <Input type="email" name="email" />
</AssistedInput>
```

A disabled or read-only field answers that it is disabled or read-only right now, and nothing changes.

## Lend a control that is not a native field

A custom control keeps its value in state, not in a DOM field. Lend it with `AssistedField`, which takes the value and a function that fills it. `options` lists the only values it takes:

```tsx
const plans = [
  { value: "free", label: "Free" },
  { value: "pro", label: "Pro" },
];

export function PlanPicker({ plan, setPlan }: { plan: string; setPlan: (plan: string) => void }) {
  return (
    <AssistedField name="plan" label="Billing plan" options={plans} value={plan} onFill={setPlan}>
      <div role="radiogroup" aria-label="Billing plan">
        {plans.map((option) => (
          <button
            key={option.value}
            type="button"
            role="radio"
            aria-checked={plan === option.value}
            onClick={() => setPlan(option.value)}
          >
            {option.label}
          </button>
        ))}
      </div>
    </AssistedField>
  );
}
```

The child is only the ring target. `onFill` gets the value the assistant chose, and the answer shows `value` once the page has rendered.

## Use the hook instead

When wrapping does not fit, call `useAssistedInput` and attach the ref it returns:

```tsx
export function CouponInput() {
  const ref = useAssistedInput<HTMLInputElement>({ name: "coupon", label: "Coupon code" });
  return <input ref={ref} name="coupon" />;
}
```

## Check it

Open the devtools' **Assisted pages** tab. The **Fields** list shows each field tool with what every field says now. See [Mount the devtools](https://superassist.app/how-to/mount-devtools.md).
