# Contact form

> A contact form built from Input, Select and Textarea, with your contact details beside it. It checks required fields, shows errors from your server on the right field, and announces the result. It makes no network call itself.

Facade UI item `contact-form` (block, section).
Docs page: https://facadeui.dev/components/contact-form · Registry JSON: https://facadeui.dev/r/contact-form.json

## Install

```bash
npx shadcn@latest add @facade/contact-form
```

Or by URL: `npx shadcn@latest add https://facadeui.dev/r/contact-form.json`. The CLI also installs what it needs: @base-ui-components/react@1.0.0-rc.0, @facade/button, @facade/container, @facade/input, @facade/section, @facade/section-header, @facade/select, @facade/textarea, @facade/types, @facade/utils.

Files: `components/sections/contact-form.tsx`

Usage example as an installable item: `npx shadcn@latest add @facade/contact-form-demo` (lands in components/examples/).

## Usage

The source of the preview on the docs page. `./content` holds sample data.

```tsx
"use client"

/** `"use client"` for the state that fakes a server round trip. */

import { useState } from "react"

import { ContactForm, type ContactFormStatus } from "@/components/sections/contact-form"

const FIELDS = [
  {
    name: "name",
    label: "Name",
    autoComplete: "name",
    required: true,
    width: "half" as const,
  },
  {
    name: "email",
    label: "Work email",
    type: "email" as const,
    autoComplete: "email",
    required: true,
    width: "half" as const,
  },
  {
    name: "topic",
    label: "What is it about?",
    type: "select" as const,
    placeholder: "Choose a topic",
    options: [
      { value: "sales", label: "Pricing and plans" },
      { value: "support", label: "Help with an install" },
      { value: "press", label: "Press" },
    ],
  },
  {
    name: "message",
    label: "Message",
    type: "textarea" as const,
    rows: 5,
    required: true,
  },
]

export function Demo() {
  const [status, setStatus] = useState<ContactFormStatus>("idle")
  const [errors, setErrors] = useState<Record<string, string>>()

  return (
    <ContactForm
      eyebrow="Contact"
      title="Talk to the team"
      description="Questions about a plan, an install or anything else. A person reads every message."
      fields={FIELDS}
      status={status}
      errors={errors}
      note="We only use your details to reply."
      details={[
        { label: "Email", value: "hello@example.com", href: "mailto:hello@example.com" },
        { label: "Phone", value: "+31 20 123 4567", href: "tel:+31201234567" },
        { label: "Reply time", value: "Within two working days" },
      ]}
      onSubmit={(values) => {
        setStatus("submitting")
        // Stands in for a server: rejects example.com addresses.
        setTimeout(() => {
          if (values.email?.endsWith("@example.com")) {
            setErrors({ email: "Use your work address, not example.com." })
            setStatus("error")
          } else {
            setErrors(undefined)
            setStatus("success")
          }
        }, 600)
      }}
    />
  )
}
```

## Props

### ContactFormProps

Extends SectionBaseProps.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` (required) | `string` |  |  |
| `eyebrow` | `string` |  |  |
| `description` | `string` |  |  |
| `fields` | `ContactField[]` | `DEFAULT_FIELDS` | The fields, in order. Defaults to name, email, company and message. |
| `onSubmit` | `(values: Record<string, string>) => void` |  | Called with the values once the browser's checks pass. |
| `status` | `ContactFormStatus` | `"idle"` | Controls the button, the live region and the error messages. |
| `errors` | `Record<string, string>` |  | Field errors from your server, keyed by field `name`. |
| `successMessage` | `ReactNode` | `"Thanks — we will reply within two working days."` | Announced on success. |
| `errorMessage` | `ReactNode` | `"That did not send. Check the form and try again."` | Announced on failure. |
| `submitLabel` | `string` | `"Send message"` |  |
| `note` | `ReactNode` |  | Small print under the button, such as how the data is used. |
| `details` | `ContactDetail[]` |  | Ways to reach you, shown beside the form. |
| `link` | `LinkComponent` |  |  |
| `variant` | `"plain" \| "muted" \| "card"` | `"card"` | `card` puts the form on a bordered surface; `muted` on a tinted one. |
