> ## Documentation Index
> Fetch the complete documentation index at: https://docs.attio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Building forms

> How to build and validate forms

Forms can be displayed inside [dialogs](../dialogs/show-dialog) using components
returned from [`useForm()`](../forms/use-form), which is given a
[form schema](../forms/form-schema).

<img className="dark:hidden" width="720" height="440" noZoom src="https://mintcdn.com/attio/4Fh2EPa8-SlLTNAV/images/dialog.png?fit=max&auto=format&n=4Fh2EPa8-SlLTNAV&q=85&s=8382f7d690adc03b3bde9da44d844d5b" data-path="images/dialog.png" />

<img className="hidden dark:block" width="720" height="440" noZoom src="https://mintcdn.com/attio/4Fh2EPa8-SlLTNAV/images/dialog-dark.png?fit=max&auto=format&n=4Fh2EPa8-SlLTNAV&q=85&s=152f14974743b47e31894f064e6e75a3" data-path="images/dialog-dark.png" />

## How to build a form

The process to build a form is:

1. Create a [form schema](../forms/form-schema).
2. Destructure the form components you need from [`useForm()`](../forms/use-form).
3. Render the components. Wrap your form inputs in a `<Form/>`.
4. Define your `onSubmit` function in `<Form/>`.

## Example: Simple Form

```tsx simple-form.tsx theme={"system"}
import {Forms, useForm, showToast} from "attio/client"

// Define your schema outside of the component
const formSchema = {
  title: Forms.string(),
  url: Forms.string().url(),
  age: Forms.number().min(18),
  approved: Forms.boolean(),
  subscribed: Forms.boolean(),
}

export function SimpleFormDialog({onDone}: {onDone: () => void}) {
  const {Form, TextInput, NumberInput, Checkbox, Toggle, SubmitButton} = useForm(formSchema, {
    // These default values are required because these strings are required
    title: "",
    url: "",
    // No defaults are obligatory for required numbers or booleans
    // They default to 0 and false respectively
  })
  return (
    <Form
      onSubmit={async (values) => {
        // Usually you'd call a server function here
        await showToast({
          title: "Form submitted",
          variant: "success",
          text: JSON.stringify(values, null, 2),
        })
        onDone()
      }}
    >
      {/* These `name` props are strongly typed to your form schema */}
      <TextInput label="Title" name="title" />
      <TextInput label="URL" name="url" />
      <NumberInput label="Age" name="age" />
      <Checkbox label="Approved" name="approved" />
      <Toggle label="Subscribed" name="subscribed" />
      {/* A `<SubmitButton/>` is required as a direct child of `<Form/>`. */}
      {/* No matter where you place it, it will be */}
      {/* rendered in the footer of the dialog. */}
      <SubmitButton label="Submit" />
    </Form>
  )
}
```

## Example: Complex validation

Sometimes you may need to add more complex validation than a static schema can express.
For example, you might want to ensure that the minimum value is less than the maximum value.
You can achieve this by passing a validation function to `useForm()`.

```tsx complex-validation.tsx theme={"system"}
import {Forms, useForm, showToast} from "attio/client"

// Define your schema outside of the component
const formSchema = {
  min: Forms.number().min(0).max(100),
  max: Forms.number().min(0).max(100),
}

export function ComplexValidationDialog({onDone}: {onDone: () => void}) {
  const {Form, NumberInput, SubmitButton} = useForm(
    formSchema,
    {},
    // As a third parameter to useForm(), you can pass a validation function
    // that takes all the form values and returns errors in the same shape
    // as the form values.
    (values) => {
      const errors = {} // empty errors object means validation passes
      if (values.min > values.max) {
        errors.min = "Must be less than max"
        errors.max = "Must be greater than than min"
      }
      return errors
    },
  )
  return (
    <Form
      onSubmit={async (values) => {
        // Usually you'd call a server function here
        await showToast({
          title: "Form submitted",
          variant: "success",
          text: JSON.stringify(values, null, 2),
        })
        onDone()
      }}
    >
      {/* These `name` props are strongly typed to your form schema */}
      <NumberInput label="Minimum" name="min" />
      <NumberInput label="Maximum" name="max" />
      {/* A `<SubmitButton/>` is required as a direct child of `<Form/>`. */}
      {/* No matter where you place it, it will be */}
      {/* rendered in the footer of the dialog. */}
      <SubmitButton label="Submit" />
    </Form>
  )
}
```

## Related documentation

* [`showDialog()`](../dialogs/show-dialog)
* [`useForm()`](../forms/use-form)
* [Form schema](../forms/form-schema)
