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

# Forms - Web

A form is a collection of related controls for users to provide data and configurations.

![Overview of a
Form](https://mintcdn.com/servicetitan/UmHUvEuYrbSv6t_f/images/docs/web/patterns/shared/overview-of-a-form.png?fit=max\&auto=format\&n=UmHUvEuYrbSv6t_f\&q=85\&s=28cc4ed8382ec03cd3b81f603209b34f)

## Form Anatomy

Forms are made up of many possible components, including many represented below.

![Anatomy of a form](https://mintcdn.com/servicetitan/KE7wVYXA9MgEh_Ye/images/docs/web/patterns/forms/anatomy-of-a-form.png?fit=max\&auto=format\&n=KE7wVYXA9MgEh_Ye\&q=85\&s=3603e59592170ab5748e29d04049f422)

1. Label

2. Description

3. More Information

4. Placeholder

5. Form Actions

## Suggested spacing

The following are suggested spacing for form elements. Spacing within the component is set by the system, while spacing between components is suggested.

![Visualization of form
spacing](https://mintcdn.com/servicetitan/UmHUvEuYrbSv6t_f/images/docs/web/patterns/forms/visualization-of-form-spacing.png?fit=max\&auto=format\&n=UmHUvEuYrbSv6t_f\&q=85\&s=d7f11badbcfca1ef2f79705843ce3b83)

##### Group spacing

![Visualization of form group
spacing](https://mintcdn.com/servicetitan/UmHUvEuYrbSv6t_f/images/docs/web/patterns/forms/visualization-of-form-group-spacing.png?fit=max\&auto=format\&n=UmHUvEuYrbSv6t_f\&q=85\&s=5915142a1e5c001a013e206c3ec0e3b8)

## Columns

forms-columns-01

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-columns-01?file=src/App.tsx)

```tsx lines theme={null}
import { Grid, TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <Grid gap="6">
      <Grid gap="6" templateColumns="1fr 1fr">
        <TextField label="First Name" />
        <TextField label="Last Name" />
      </Grid>
      <TextField label="Email Address" />
    </Grid>
  );
}

export default App;
```

forms-columns-02

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-columns-02?file=src/App.tsx)

```tsx lines theme={null}
import { Grid, TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <Grid gap="6">
      <TextField label="First Name" />
      <TextField label="Last Name" />
      <TextField label="Email Address" />
    </Grid>
  );
}

export default App;
```

forms-columns-03

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-columns-03?file=src/App.tsx)

```tsx lines theme={null}
import { Grid, TextField, Radio } from "@servicetitan/anvil2";

function App() {
  return (
    <Grid gap="6" templateColumns="1fr 1fr">
      <TextField label="Your Name" />
      <TextField label="Email Address" />

      <Radio.Group legend="My function is">
        <Radio name="role" value="designer" label="Designer" />
        <Radio name="role" value="developer" label="Developer" />
        <Radio name="role" value="pm" label="Product Manager" />
      </Radio.Group>
      <Radio.Group legend="What support do you need?">
        <Radio name="support" value="software" label="Software" />
        <Radio name="support" value="hardware" label="Hardware" />
        <Radio name="support" value="report" label="Expense Report" />
        <Radio name="support" value="tools" label="Learning Tools" />
      </Radio.Group>
    </Grid>
  );
}

export default App;
```

As a rule, form elements should be limited to 1 column. Multi-column layouts can be used when there is sufficient space on the screen for it, and the elements are logically related to each other.

## Forms in Layouts

##### In a Dialog

As a rule, form element width should conform to the size of the Dialog’s set width.

forms-dialog-width

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-dialog-width?file=src/App.tsx)

```tsx lines theme={null}
import { Button, Dialog, Grid, TextField } from "@servicetitan/anvil2";
import { useState } from "react";

function App() {
  const [open, setOpen] = useState(true);

  return (
    <div style={{ minWidth: "35.5rem", minHeight: "20.5rem" }}>
      <Button onClick={() => setOpen(true)}>Open to show Dialog example</Button>
      <Dialog size="large" open={open} onClose={() => setOpen(false)}>
        <Dialog.Header>Partner Information</Dialog.Header>
        <Dialog.Content>
          <Grid gap="6" style={{ width: "100%" }}>
            <Grid gap="6" templateColumns="1fr 1fr">
              <TextField label="First Name" />
              <TextField label="Last Name" />
            </Grid>
            <TextField label="Email Address" />
          </Grid>
        </Dialog.Content>
        <Dialog.Footer>
          <Button onClick={() => setOpen(false)}>Cancel</Button>
          <Button appearance="primary" onClick={() => setOpen(false)}>
            Save
          </Button>
        </Dialog.Footer>
      </Dialog>
    </div>
  );
}

export default App;
```

## Labels and Help

### What help elements should we use?

| Element | When to use | Preference |
| - | - | - |
| Label | Used in most situations. Provides essential information to a user in 1–3 words. | 1st |
| Hint Text | Hints at what the input should be for the field. | 2nd |
| Description | Describes what the field is about and/or what impact it would make to what users care about. | 3rd |
| More Info | When providing supplemental information, or lengthy explanations. | 4th |
| Placeholder | In general, avoid. May be used to hint at text field’s formatting. | 5th |

### Labels should almost always be used

Most contexts for forms require a label. Some exceptions include when an icon can clearly explain the use case, such as a search input, or if a label exists elsewhere, such as a table column.

forms-label-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-label-do?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return <TextField label="Email Address" />;
}

export default App;
```

> [!TIP]
>
> **Do**

forms-label-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-label-dont?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return <TextField placeholder="Email Address" />;
}

export default App;
```

> [!CAUTION]
>
> **Don't**

### Use inline help for essential and supplementary information

Descriptions are strong at providing additional information necessary to complete a form.

forms-description-01

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-description-01?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField
      label="Password"
      type="password"
      hint="Must be at least 8 characters long."
    />
  );
}

export default App;
```

forms-description-02

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-description-02?file=src/App.tsx)

```tsx lines theme={null}
import { TextField, Text, Link } from "@servicetitan/anvil2";
import { core } from "@servicetitan/anvil2/token";

function App() {
  return (
    <TextField
      label="Office Phone"
      description={
        <Text size="small" subdued>
          This is the number customers see when a call is made.{" "}
          <Link
            style={{ marginTop: core.primitive?.Size1?.value }}
            appearance="secondary"
          >
            Learn more
          </Link>
        </Text>
      }
    />
  );
}

export default App;
```

### Use more info for supplementary information

More info help can de-clutter a page by putting supplemental information behind an overlay element.

forms-moreinfo-01

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-moreinfo-01?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField
      label="Password"
      type="password"
      hint="Must be at least 8 characters long."
      moreInfo="If you are having trouble logging in, contact our customer support team."
    />
  );
}

export default App;
```

forms-moreinfo-02

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-moreinfo-02?file=src/App.tsx)

```tsx lines theme={null}
import { Flex, Checkbox, Tooltip, Icon } from "@servicetitan/anvil2";
import Info from "@servicetitan/anvil2/assets/icons/material/round/info.svg";
import { useState } from "react";

function App() {
  const [isFocused, setIsFocused] = useState(false);
  return (
    <Flex gap="1">
      <Checkbox
        id="cb3"
        label="Academy company admin"
        onFocus={() => setIsFocused(true)}
        onBlur={() => setIsFocused(false)}
      />
      <Tooltip
        open={isFocused}
        placement="right"
        fallbackPlacements={["bottom"]}
      >
        <Tooltip.Trigger style={{ alignSelf: "center" }}>
          {/* Don't need Icon to be focusable because Checkbox focus triggers it already */}
          <Icon
            svg={Info}
            onMouseEnter={() => setIsFocused(true)}
            onMouseLeave={() => setIsFocused(false)}
          />
        </Tooltip.Trigger>
        <Tooltip.Content>
          Can assign courses to employees, track Academy progress, and run
          reports
        </Tooltip.Content>
      </Tooltip>
    </Flex>
  );
}

export default App;
```

### Hints and Descriptions are preferred over more info

Hint text and description text always being visible on the page improves the usability of the content itself. More info can still be used however, particularly when the content density of the page is high.

### Relationship between Hint, Description, and Error text

##### Hint Text

Hint text primarily focuses on the expected format for an input. It appears before a user interacts with a form field or when the field is empty.

forms-relationship-hint

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-relationship-hint?file=src/App.tsx)

```tsx lines theme={null}
import { DateFieldSingle } from "@servicetitan/anvil2";

function App() {
  return (
    <DateFieldSingle
      label="Appointment Date"
      description="Select the date you'd like to book your service."
    />
  );
}

export default App;
```

##### Description Text

Description text provides broader context about what the form field is about or what impact it would make. It appears after the Hint text. It helps users understand why they are providing this information and the implications of their input.

forms-relationship-description

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-relationship-description?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField
      label="Shipping Address"
      description="This address will be used to deliver your order"
    />
  );
}

export default App;
```

##### Error Text

Error text appears when a user's input fails validation. Hint text remains visible alongside the error to provide persistent contextual guidance. Error text clearly and concisely describes what went wrong and often suggests a way to correct the issue. Description text persists regardless. Multiple error messages and [warning messages](https://anvil.servicetitan.com/docs/components/field-message/code) are also supported.

The combination of Hint, Error, Warning, and Description text should be kept concise, where Hint is typically one line and Description is at most three.

forms-relationship-error

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-relationship-error?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField
      label="Shipping Address"
      description="This address will be used to deliver your order"
      required
      error="Please enter your street address"
    />
  );
}

export default App;
```

### Placeholder text should generally be avoided

In general, placeholders should be omitted, as they have many downsides:

* Users lose the help content on typing, and after the element is filled out.
* Higher contrast text can confuse a user into believing the placeholder is a real value.
* They have inconsistent implementations in browsers.

forms-avoidplaceholder-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-avoidplaceholder-dont?file=src/App.tsx)

```tsx lines theme={null}
import { TextField, Icon } from "@servicetitan/anvil2";
import Person from "@servicetitan/anvil2/assets/icons/material/round/person.svg";

function App() {
  return (
    <TextField
      placeholder="Full name"
      prefix={
        (
          <Icon aria-label="search icon" svg={Person} size="large" />
        ) as unknown as string
      }
    />
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

forms-avoidplaceholder-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-avoidplaceholder-do?file=src/App.tsx)

```tsx lines theme={null}
import { SearchField } from "@servicetitan/anvil2";

function App() {
  return <SearchField placeholder="Search equipment" />;
}

export default App;
```

> [!TIP]
>
> **Do**

Placeholder usage in a search field is allowed.

### Reassure the user as they take action

Form help reminds and validates the user’s actions. It’s especially valuable when used with features that aren’t used often, have high stakes, or rely on a certain level of expertise and area knowledge, e.g., accounting concepts.

This helps the user feel confident about the outcome what they are about to do and reduce errors.

## Form Validation

To see how to handle errors in Forms, refer to our [Errors & Warning pattern](https://anvil.servicetitan.com/docs/patterns/errors-and-warnings/web.md) documentation.

forms-validation

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-validation?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField
      label="Password"
      type="password"
      hint="Must be at least 8 characters long."
      error="Password must be at least 8 characters."
    />
  );
}

export default App;
```

## Form Submission

Forms should only submit when the user explicitly clicks the submit button. Pressing the Enter key within a form field should not trigger form submission. This prevents accidental submissions, especially when users are still reviewing or completing other fields in the form.

> [!TIP]
>
> **Do**

Require users to click the submit button to submit the form.

> [!CAUTION]
>
> **Don't**

Allow pressing Enter in a text field to submit the form.

## Progressive Disclosure

Certain form elements can be displayed only after a relevant element has been selected. This can be useful in shortening a form without losing critical information. When these hidden elements are inline, indenting the element can visually reinforce its relation.

forms-disclosure

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-disclosure?file=src/App.tsx)

```tsx lines theme={null}
import { Radio, TextField } from "@servicetitan/anvil2";
import { useState } from "react";

function App() {
  const [radioState, setRadioState] = useState("other");

  return (
    <Radio.Group legend="I am a...">
      <Radio
        name="exampleRadioOther"
        value="software"
        label="Designer"
        checked={radioState === "software"}
        onChange={(_v) => setRadioState("software")}
      />
      <Radio
        name="exampleRadioOther"
        value="hardware"
        label="Developer"
        checked={radioState === "hardware"}
        onChange={(_v) => setRadioState("hardware")}
      />
      <Radio
        name="exampleRadioOther"
        value="report"
        label="Product Manager"
        checked={radioState === "report"}
        onChange={(_v) => setRadioState("report")}
      />
      <Radio
        name="exampleRadioOther"
        value="tools"
        label="Other"
        checked={radioState === "other"}
        onChange={(_v) => setRadioState("other")}
      />
      {radioState === "other" && (
        <div
          style={{ marginLeft: "2rem", marginTop: "-0.25rem", width: "15rem" }}
        >
          {" "}
          <TextField />{" "}
        </div>
      )}
    </Radio.Group>
  );
}

export default App;
```

## Button Alignment

##### Place form actions at the bottom

forms-buttonalignment-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-buttonalignment-do?file=src/App.tsx)

```tsx lines theme={null}
import { Grid, TextField, Flex, Button } from "@servicetitan/anvil2";
import { core } from "@servicetitan/anvil2/token";

function App() {
  return (
    <Grid gap="6">
      <Grid gap="6" templateColumns="1fr 1fr">
        <TextField label="First Name" />
        <TextField label="Last Name" />
      </Grid>
      <TextField label="Email Address" />

      <Flex
        gap="3"
        style={{
          marginTop: core.primitive?.Size6?.value,
        }}
      >
        <Button appearance="primary">Submit</Button>
        <Button>Cancel</Button>
      </Flex>
    </Grid>
  );
}

export default App;
```

> [!TIP]
>
> **Do**

forms-buttonalignment-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-buttonalignment-dont?file=src/App.tsx)

```tsx lines theme={null}
import { Grid, TextField, Flex, Button } from "@servicetitan/anvil2";
import { core } from "@servicetitan/anvil2/token";

function App() {
  return (
    <Grid gap="6">
      <Flex
        gap="3"
        style={{
          marginBottom: core.primitive?.Size6?.value,
        }}
      >
        <Button appearance="primary">Submit</Button>
        <Button>Cancel</Button>
      </Flex>
      <Grid gap="6" templateColumns="1fr 1fr">
        <TextField label="First Name" />
        <TextField label="Last Name" />
      </Grid>
      <TextField label="Email Address" />
    </Grid>
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

##### Left alignment usage

Left alignment is used when the form is on a typical page.

![Example of a left aligned
button.](https://mintcdn.com/servicetitan/UmHUvEuYrbSv6t_f/images/docs/web/patterns/forms/example-of-a-left-aligned-button.png?fit=max\&auto=format\&n=UmHUvEuYrbSv6t_f\&q=85\&s=32dbaca8e41a076a9803d8f14d515544)

##### Right alignment usage

Right-aligned actions occur when the form element is contained in a Modal or Drawer, or when a page has a sticky footer.

![Example of a right aligned button in a
Dialog.](https://mintcdn.com/servicetitan/UmHUvEuYrbSv6t_f/images/docs/web/patterns/forms/example-of-a-right-aligned-button-in-a-dialog.png?fit=max\&auto=format\&n=UmHUvEuYrbSv6t_f\&q=85\&s=b6bf92c0284174a27d878ba0810f8dbd)

![Example of a right aligned button in a
Drawer.](https://mintcdn.com/servicetitan/UmHUvEuYrbSv6t_f/images/docs/web/patterns/forms/example-of-a-right-aligned-button-in-a-drawer.png?fit=max\&auto=format\&n=UmHUvEuYrbSv6t_f\&q=85\&s=9c22b852e4581703d33483c666e58c5f)

![Example of a right aligned button in a
Page.](https://mintcdn.com/servicetitan/UmHUvEuYrbSv6t_f/images/docs/web/patterns/forms/example-of-a-right-aligned-button-in-a-page.png?fit=max\&auto=format\&n=UmHUvEuYrbSv6t_f\&q=85\&s=76a3f6822ade644757b7395eb8df4641)

## Form Titles

Titles should clearly communicate at a glance what the purpose of the form is.

* Job Dashboard
* Agreement Details
* Visits

> [!TIP]
>
> **Do**

* General Info
* Details
* Other

> [!CAUTION]
>
> **Don't**

## Labels

* Keeps labels 1–3 words long

* Use title case capitalization

* Email Address

* Phone Number

* First Name

> [!TIP]
>
> **Do**

* What is your email address?
* My phone number is:
* First name

> [!CAUTION]
>
> **Don't**

##### Required Labeling

Form elements can be additionally labeled as required (\*). The way to use this includes:

* Be consistent within the complete form. This overrides preference on individual sections / pages of a form.
* Use the required (\*) when optional fields are present.
* When all fields are required, the (\*) is not needed.
* If an existing form uses the (optional) label, consider refactoring the page to use the above required labeling standards.

forms-required

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-required?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return <TextField label="Serial Number" required />;
}

export default App;
```

## Description

##### Use sentence case capitalization

##### Keep help text 1-2 lines as long as the element itself

Most contexts for forms require a label. Some exceptions include when an icon can clearly explain the use case, such as a search input, or if a label exists elsewhere, such as a table column.

forms-description-lines-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-description-lines-do?file=src/App.tsx)

```tsx lines theme={null}
import { TextField, Text, Link } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField
      label="Office Phone"
      description={
        <Text size="small" subdued>
          This is the number customers see when a call is made.{" "}
          <Link appearance="secondary">Learn more</Link>
        </Text>
      }
    />
  );
}

export default App;
```

forms-description-lines-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-description-lines-dont?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField
      label="Office Phone"
      description="Employee contact number. When an employee clicks a customer’s number to make an outbound call, this is the number that ServiceTitan dials. This cannot be a ServiceTitan tracking number."
    />
  );
}

export default App;
```

## Hint

##### Model text inputs

Modeled text inputs are text field inputs that require text to be formatted in a specific way. Because modeled text inputs require a particular structure, always include examples that demonstrate how the user should enter the information.

* Use help text to include an instructional call to action and an example that shows the required text format
* If there’s not enough room to include both an instructional call to action and an example, then include only the example
* Use the word “Example” followed by a colon to introduce the example (instead of e.g.)

forms-hint-modeled-do-01

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-hint-modeled-do-01?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return <TextField label="Domain" hint="Example: domain.com" />;
}

export default App;
```

> [!TIP]
>
> **Do**

forms-hint-modeled-dont-01

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-hint-modeled-dont-01?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField
      label="Domain"
      placeholder="name@domain.com"
      hint="Enter the domain you want to buy"
    />
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

forms-hint-modeled-do-02

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-hint-modeled-do-02?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField
      label="Option 1"
      hint="Separate options with a comma.
  Example: XS, M, L, XL, XXL"
    />
  );
}

export default App;
```

> [!TIP]
>
> **Do**

forms-hint-modeled-dont-02

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-hint-modeled-dont-02?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField label="Option 1" placeholder="Separate options with a comma" />
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

## More Information

##### Keep help text 1-2 lines

This is true even in a form with lots of more infos. Either find unique supplemental information to discuss, or remove the more info.

forms-moreinfo-lines

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-moreinfo-lines?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return (
    <Textarea
      label="Item Description"
      moreInfo="Description will appear on invoices with this item"
    />
  );
}

export default App;
```

##### Don’t repeat the content of the label

This is true even in a form with lots of overlay help. Either find unique supplemental information to discuss, or remove the overlay help.

forms-moreinfo-repeat-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-moreinfo-repeat-dont?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField
      label="Office Phone"
      moreInfo="Enter your office phone number."
      defaultValue="(888) 123–4567"
    />
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

##### Don't surface essential information

Users have to recall overlay information when completing the form element. Hint text and descriptions let the user read it at all times.

forms-moreinfo-essential-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-moreinfo-essential-dont?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField
      label="Password"
      moreInfo="Must be at least 8 characters long."
    />
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

## Placeholder Text

##### In general, avoid

In most use cases, the inline and overlay help are preferred picks to placeholder text. Placeholder text is low contrast, it disappears as soon as a user starts typing, is limited in space, and unreliable for screen readers.

##### Don’t use real examples

Real examples confuse user input. If an example is used, it should look clearly generic.

forms-placeholder-example-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-placeholder-example-dont?file=src/App.tsx)

```tsx lines theme={null}
import { TextField } from "@servicetitan/anvil2";

function App() {
  return (
    <TextField
      label="Office Phone"
      moreInfo="Enter your office phone number."
      placeholder="(555) 867–5309"
    />
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

##### In empty Selects, can describe the action taken

This would be typically be worded as “Select \_\_\_”

forms-placeholder-select-action

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/forms-placeholder-select-action?file=src/App.tsx)

```tsx lines theme={null}
import { Combobox, Grid } from "@servicetitan/anvil2";

function App() {
  return (
    <Grid gap="6" style={{ minWidth: "384px" }}>
      <Combobox items={[]}>
        <Combobox.SearchField
          label="Business Unit"
          placeholder="Select a Business Unit"
        />
      </Combobox>

      <Combobox items={[]}>
        <Combobox.SearchField
          label="Technician"
          placeholder="Select a Technician"
        />
      </Combobox>

      <Combobox items={[]}>
        <Combobox.SearchField label="View by" />
      </Combobox>
    </Grid>
  );
}

export default App;
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.