Forms can be displayed inside dialogs using components returned from useForm(), which is given a form schema.

Example: Simple Form

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

// Form schema is constant (it cannot be changed via props)
// so we should define it 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>
  );
}
The process to create a form is:
  1. Create a form schema
  2. Destructure the form components you need from useForm()
  3. Wrap your form inputs in the <Form/> tag
  4. Define your onSubmit function in <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().
complex-validation.tsx
import {Forms, useForm, showToast} from "attio/client"

// Form schema is constant (it cannot be changed via props)
// so we should define it 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 API documentation: