---
title: "TanStack Form"
description: "The fields bound to TanStack Form (from the form entry): declare the form once with useAppForm, and each field is one line that shows its value and its first error once touched. SubmitButton is pending while the form saves; FormError says why a submission was refused."
---

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

# TanStack Form

## Invite a teammate

Submit it empty to see every error, or with an email on example.org to see a refusal.

```tsx
import { useState } from 'react';
import type { Key } from 'react-aria-components';
import { SelectItem } from '@arbol-ai/ui';
import { useAppForm } from '@arbol-ai/ui/form';

interface Invitation {
  email: string;
  role: Key | null;
  notify: boolean;
}

const NO_INVITATION: Invitation = { email: '', role: null, notify: true };

const InviteForm = () => {
  const [sentTo, setSentTo] = useState('');
  const form = useAppForm({
    defaultValues: NO_INVITATION,
    onSubmit: ({ value }) => {
      setSentTo(value.email);
    },
    validators: {
      onSubmit: ({ value }) =>
        value.email.endsWith('@example.org') ? 'Invitations to example.org are turned off.' : undefined,
    },
  });
  return (
    <form.AppForm>
      <form.Form aria-label="Invite a teammate" className="max-w-sm">
        <form.AppField
          name="email"
          validators={{
            onBlur: ({ value }) =>
              /^\S+@\S+\.\S+$/u.test(value) ? undefined : 'Enter an email like ana@example.com.',
          }}
        >
          {(field) => (
            <field.TextField description="We send the invitation there." label="Email" type="email" />
          )}
        </form.AppField>
        <form.AppField
          name="role"
          validators={{ onSubmit: ({ value }) => (value === null ? 'Choose a role.' : undefined) }}
        >
          {(field) => (
            <field.Select label="Role">
              <SelectItem id="front-desk">Front desk</SelectItem>
              <SelectItem id="doctor">Doctor</SelectItem>
              <SelectItem id="admin">Administrator</SelectItem>
            </field.Select>
          )}
        </form.AppField>
        <form.AppField name="notify">
          {(field) => (
            <field.Switch description="By email, once a day.">Notify about new conversations</field.Switch>
          )}
        </form.AppField>
        <form.FormError />
        <form.SubmitButton className="self-start">Send invitation</form.SubmitButton>
        <output className="text-muted-foreground text-sm">
          {sentTo === '' ? null : `Invitation sent to ${sentTo}.`}
        </output>
      </form.Form>
    </form.AppForm>
  );
};

<InviteForm />
```

## Source

The examples above are [`src/form/app-form.examples.tsx`](https://github.com/getarbol/project-arbol-ui/blob/main/src/form/app-form.examples.tsx), which the library's tests render too.

Source: https://epidermis.getarbol.com/components/forms/app-form/index.mdx
