> ## 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.

# Date Field Range - Web

> A form field for selecting a date range with start and end dates.

> [!NOTE]
>
> For development, import `DateFieldRange` from
> `@servicetitan/anvil2`. For design, find it in the
> [Anvil2 file](https://www.figma.com/design/xaNQsvRKczoxu7pj0M6QT8/Anvil2-v3?node-id=2030-3775).

![](https://mintcdn.com/servicetitan/66tZkkkyD48ugyZL/images/docs/web/components/date-field-range/overview-image.png?fit=max\&auto=format\&n=66tZkkkyD48ugyZL\&q=85\&s=ce95de2e1880ad0e063dcce5e3705927)

## Anatomy

Date Field Range consists of four primary elements that enable users to select a start and end date in one masked field, with a calendar for visual selection.

![Anatomy of the Date Field Range
component](https://mintlify.s3.us-west-1.amazonaws.com/servicetitan/images/docs/web/components/date-field-range/web/date-field-range-anatomy.png)

1. **Label** - Identifies the field and its purpose
2. **Start date** - First segment of the masked field
3. **End date** - Second segment of the same field, after a hyphen
4. **Helper text** (Optional) - Description, format hint, warning, or error message

## Options

Date Field Range supports the following configurations to accommodate different use cases and regional preferences.

### Date Format Modes

datefieldrange-mode

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

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

function App() {
  return (
    <Flex direction="column" gap="4" style={{ minWidth: "384px" }}>
      {/** US format (default) */}
      <DateFieldRange label="US Format" mode="mm/dd/yyyy" />

      {/** International format */}
      <DateFieldRange label="International Format" mode="dd/mm/yyyy" />
    </Flex>
  );
}

export default App;
```

Date Field Range supports two date format modes to match regional conventions. The default format is `mm/dd/yyyy` for US audiences, while `dd/mm/yyyy` is available for international users.

| Mode | Format | Use Case |
| - | - | - |
| mm/dd/yyyy | Default | US date format |
| dd/mm/yyyy | Optional | International date format |

### Sizes

datefieldrange-size

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

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

function App() {
  return (
    <Flex direction="column" gap="4" style={{ minWidth: "384px" }}>
      <DateFieldRange label="Small" size="small" />
      <DateFieldRange label="Medium" size="medium" />
      <DateFieldRange label="Large" size="large" />
    </Flex>
  );
}

export default App;
```

Three size options accommodate different layout densities and visual hierarchies.

| Size | Height |
| - | - |
| Small | 32px |
| Medium | 40px |
| Large | 48px |

## Behavior

Date Field Range responds to user interaction with distinct visual states and validation feedback.

### Visual States

datefieldrange-visual-states

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

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

function App() {
  return (
    <Grid gap="6">
      <DateFieldRange
        label="Reporting Period"
        defaultValue={{
          startDate: "2025-01-01",
          endDate: "2025-12-31",
        }}
        required
      />
      <DateFieldRange
        label="Reporting Period"
        defaultValue={{
          startDate: "2025-01-01",
          endDate: "2025-12-31",
        }}
        required
        disabled
      />
      <DateFieldRange
        label="Reporting Period"
        defaultValue={{
          startDate: "2025-12-31",
          endDate: "2025-01-01",
        }}
        required
        error="End date must be after start date"
      />
    </Grid>
  );
}

export default App;
```

The component displays different visual states to communicate field status and interactivity. Default state shows the field ready for input. Focus state highlights the active field. Error state shows a message the product supplies. The field does not invent that message from the dates alone. Disabled state prevents interaction and closes an open calendar.

### Manual entry

Both dates are typed into one field. The mask shows `mm/dd/yyyy - mm/dd/yyyy` (or `dd/mm/yyyy - dd/mm/yyyy` when that mode is selected), with a space, hyphen, and space between the ends. A single digit followed by `/` is padded, so `1/` becomes `01/`. The field does not swap an end date that falls before the start date. That order stays as typed, and the range fails constraint validation.

### Calendar interaction

Clicking the field opens the calendar and leaves focus in the input. Arrow Down opens the calendar and moves focus to the first control inside it. Users can move between months and select each end of the range. The calendar highlights the selected span and closes after the end date is selected, returning focus to the field. Choosing a day before the current start restarts the range at that day instead of storing an inverted pair. Dates outside the minimum and maximum, and dates marked unavailable, are disabled in the calendar.

### Validation

datefieldrange-validation

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

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

function App() {
  return (
    <DateFieldRange
      label="Reporting Period"
      error="End date must be after start date"
      required
    />
  );
}

export default App;
```

Date Field Range checks the range as it changes and reports whether the text is complete and whether the range is allowed. It does not write an error message itself. The message under the field is the one the product provides.

A range is complete when both ends are real dates. It is allowed when the last committed dates fall on available days, stay inside the minimum and maximum, and the start is not after the end. An empty range is allowed unless the field is required. An in-progress edit can still report the previous range as allowed, so wait until both dates are complete before showing a constraint error.

## Usage Guidelines

| Standard | Requirement |
| - | - |
| DoCapturing a date span | Capture a date span with both start and end dates |
| DoProviding calendar context | Provide calendar visual context for range selection |
| DoSupporting manual and visual entry | Support both manual entry and visual selection |
| DoValidating date ranges | Validate date ranges against constraints like minimum or maximum dates |
| DoLabel date ranges clearly | Write labels that clearly communicate the purpose of the range, include format hints, validate that start is before end, and show errors immediately; see [Labeling and Range Validation](#labeling-and-range-validation). |
| Don'tSingle dates | Use [Date Field Single](https://anvil.servicetitan.com/docs/components/date-field-single/web.md) instead |
| Don'tRecurring date ranges without year | Use [Date Field Yearless Range](https://anvil.servicetitan.com/docs/components/date-field-yearless-range/web.md) for seasonal ranges or annual periods |
| Don'tTime selection | Use [Time Field](https://anvil.servicetitan.com/docs/components/time-field/web.md) for time-only inputs |
| Don'tAmbiguous context or invalid ranges | Don't omit format guidance or allow invalid date ranges without clear feedback; see [Anti-pattern: Ambiguous Context and Invalid Ranges](#anti-pattern%3A-ambiguous-context-and-invalid-ranges). |

### Alternatives

#### Date Field Range vs Date Field Yearless Range

Use Date Field Range when the year is essential to the date range selection, such as project timelines or reporting periods. Use Date Field Yearless Range when the year is irrelevant, such as recurring seasonal periods or annual date templates.

### How to Use

#### Labeling and Range Validation

Provide clear, descriptive labels that communicate the purpose of the date range selection. Labels should indicate what the range represents and establish the relationship between start and end dates. Implement validation that ensures logical date ranges and provides immediate feedback when constraints are violated.

datefieldrange-reporting-period

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

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

function App() {
  return (
    <DateFieldRange
      label="Date Range"
      description="Select the start and end dates for this period"
      required
    />
  );
}

export default App;
```

**Do**

* Write labels that clearly communicate the purpose of the date range selection
* Keep the format hint visible when the date format might be unfamiliar
* Set minimum and maximum dates to block dates outside the allowed span
* Show an error when both dates are complete and the range still fails its constraints
* Explain that the end date must be on or after the start date
* Use the calendar when visual selection is helpful

#### Anti-pattern: Ambiguous Context and Invalid Ranges

Avoid creating confusion by omitting format guidance or allowing invalid date ranges without clear feedback.

datefieldrange-recurring-periods-dont

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

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

function App() {
  return <DateFieldRange label="Period" required />;
}

export default App;
```

**Don’t**

* Use Date Field Range for recurring annual periods where year doesn't matter
* Hide the format hint when the date format might be unfamiliar
* Treat the first typed digit as a failed range
* Leave a finished, inverted range without an error message
* Use placeholder text to show the date format — the mask already fills the field, so a placeholder is never visible
* Create labels that don't clearly indicate what date range is being selected

## Content

Content within Date Field Range should be clear, concise, and actionable.

* Labels should clearly indicate what date range is being selected
* Error messages should explain why a date range is invalid and how to fix it
* Format hints should match the selected mode (mm/dd/yyyy or dd/mm/yyyy)
* Descriptions should provide context without overwhelming the user

## Keyboard Interaction

Users can navigate Date Field Range using standard keyboard controls.

### Input Field

| Key | Description |
| - | - |
| Tab | Moves focus to the next element |
| Shift + Tab | Moves focus to the previous element |
| Arrow Down | Opens the calendar and moves focus into it |
| Esc | Closes the calendar picker |

### Calendar Picker

| Key | Description |
| - | - |
| Arrow Left | Moves focus to the previous day |
| Arrow Right | Moves focus to the next day |
| Arrow Up | Moves focus to the same day in the previous week |
| Arrow Down | Moves focus to the same day in the next week |
| Page Up | Moves focus to the same day in the previous month |
| Page Down | Moves focus to the same day in the next month |
| Shift + Page Up | Moves focus to the same day in the previous year |
| Shift + Page Down | Moves focus to the same day in the next year |
| Enter | Selects the focused date (for range selection) |
| Space | Selects the focused date (for range selection) |
| Esc | Closes the calendar picker |

### Accessibility

Date Field Range includes built-in accessibility features. The component supports full keyboard navigation and screen reader compatibility. Ensure labels are descriptive and error messages are clear for assistive technology users.

For more guidance on form field labels and context, see [input field context association best practices](https://anvil.servicetitan.com/docs/accessibility/labels-and-ctas.md#input-field-context-association).

## Code

**Implementation**

datefieldrange-playground

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

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

function App() {
  return <DateFieldRange />;
}

export default App;
```

## Common Examples

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

function ExampleComponent() {
  return (
    <DateFieldRange
      label="Reporting period"
      defaultValue={{
        startDate: "1999-12-25",
        endDate: "2000-01-01",
      }}
    />
  );
}
```

### Date string values

`DateFieldRange` accepts and returns [date-only ISO 8601 strings](https://anvil.servicetitan.com/docs/utilities/web/date-values.md) (`YYYY-MM-DD`) for `startDate` and `endDate`. `onChange` returns that same date-only form, or `null` when an end is empty. A full ISO datetime is accepted and normalized to the calendar date in that string's offset; the day in the string is kept. When you start from a JavaScript `Date`, build the string from the local year, month, and day. `toISOString()` is UTC, so it can name a different calendar date than the local one you mean to store.

The field is one masked input, not two. The displayed value looks like `mm/dd/yyyy - mm/dd/yyyy`, with a space, hyphen, and space between the ends. A single digit followed by `/` is zero-padded (`1/` becomes `01/`). Typing does not swap an end date that is earlier than the start.

### Default values

Pass `defaultValue` for an uncontrolled field. The field keeps later edits itself. Use `null` to start empty.

datefieldrange-defaultvalue

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

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

function App() {
  return (
    <DateFieldRange
      label="Reporting period"
      defaultValue={{
        startDate: "1999-12-25",
        endDate: "2000-01-01",
      }}
    />
  );
}

export default App;
```

### Controlled usage

Store a `DateFieldRangeValue` in state and pass it as `value`. `onChange` receives a `DateFieldRangeChange`, which adds validity flags the `value` prop does not accept. Copy `startDate` and `endDate` back into state. Pass `null` to clear both ends.

datefieldrange-controlled

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

```tsx lines expandable theme={null}
import {
  DateFieldRange,
  Flex,
  Button,
  type DateFieldRangeValue,
} from "@servicetitan/anvil2";
import { useState } from "react";

function toDateString(date: Date): string {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}

function App() {
  const [value, setValue] = useState<DateFieldRangeValue>(null);

  const today = new Date();
  const plus5days = new Date();
  plus5days.setDate(today.getDate() + 5);

  return (
    <Flex gap={4}>
      <DateFieldRange
        label="Reporting period"
        value={value}
        onChange={(change) =>
          setValue({ startDate: change.startDate, endDate: change.endDate })
        }
      />
      <Button
        onClick={() =>
          setValue({
            startDate: toDateString(today),
            endDate: toDateString(plus5days),
          })
        }
      >
        Set to today + 5 days
      </Button>
    </Flex>
  );
}

export default App;
```

### Constraint errors

The field reports validity and leaves the message to you. `isInputValid` is true only when both ends are complete dates. `isDateRangeValid` is a separate check on the last committed dates: an empty range passes unless `required` is set. An in-progress edit can leave `isDateRangeValid` true while `isInputValid` is false, because an incomplete end keeps the previous committed date. Show `error` only after `isInputValid` is true and the range is still not allowed. `error` also sets `aria-invalid`. A boolean `true` is the error style with no message.

datefieldrange-constraint-error

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

```tsx lines expandable theme={null}
import { DateFieldRange, type DateFieldRangeValue } from "@servicetitan/anvil2";
import { useState } from "react";

function App() {
  const [value, setValue] = useState<DateFieldRangeValue>({
    startDate: "2025-12-31",
    endDate: "2025-01-01",
  });
  const [error, setError] = useState<string | undefined>(
    "Enter an end date on or after the start date, inside the allowed range.",
  );

  return (
    <DateFieldRange
      label="Reporting period"
      minDate="2025-01-01"
      maxDate="2025-12-31"
      value={value}
      error={error}
      onChange={(change) => {
        setValue({ startDate: change.startDate, endDate: change.endDate });

        // Partial entry is not a finished range. Wait until both ends parse.
        if (!change.isInputValid) {
          setError(undefined);
          return;
        }

        setError(
          change.isDateRangeValid
            ? undefined
            : "Enter an end date on or after the start date, inside the allowed range.",
        );
      }}
    />
  );
}

export default App;
```

### Disabled hint text

The hint text can be disabled. If the hint text is disabled, ensure that the date format is displayed on the page.

datefieldrange-disablehint

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

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

function App() {
  return <DateFieldRange disableHint />;
}

export default App;
```

### Timezone

`timezone` sets the calendar's Today action and, when the field has no value, the month it opens on. When the zone differs from the browser time zone, the calendar shows "Dates shown in" plus that zone's abbreviation.

datefieldrange-timezone

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

```tsx lines expandable theme={null}
import { DateFieldRange } from "@servicetitan/anvil2";
import { useState } from "react";
import type { DateFieldRangeValue } from "@servicetitan/anvil2";

function App() {
  const [value, setValue] = useState<DateFieldRangeValue>(null);

  return (
    <DateFieldRange
      label="Booking Dates"
      timezone="Australia/Sydney"
      value={value}
      onChange={(change) =>
        setValue({ startDate: change.startDate, endDate: change.endDate })
      }
    />
  );
}

export default App;
```

### Disable calendar popover

The display of the `Calendar` can be prevented by passing `disableCalendar`.

datefieldrange-disablecalendar

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

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

function App() {
  return <DateFieldRange disableCalendar />;
}

export default App;
```

### Markdown in labels

The `label` prop supports inline markdown: bold (`**text**`), italic (`*text*`), bold and italic (`***text***`), highlight (`==text==`), and code (`` `text` ``).

datefieldrange-markdownlabel

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

```tsx lines expandable theme={null}
import { DateFieldRange, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex direction="column" gap="4" style={{ maxWidth: 500 }}>
      <DateFieldRange label="**Bold** label" />
      <DateFieldRange label="*Italic* label" />
      <DateFieldRange label="***Bold and italic*** label" />
      <DateFieldRange label="==Highlight== label" />
      <DateFieldRange label="`Code` label" />
    </Flex>
  );
}

export default App;
```

### Hide the label

Use `hideLabel` to visually hide the label. The `label` string is converted to an `aria-label` on the input so it remains accessible to screen readers — any inline markdown is stripped to plain text.

datefieldrange-hidelabel

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

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

function App() {
  return <DateFieldRange label="Date range" hideLabel />;
}

export default App;
```

## React Accessibility

The input is a textbox. With a string `label`, the visible label is associated with the input. `hideLabel` moves that string to `aria-label` and strips inline markdown. With no `label`, the input's accessible name is "Select a date range".

`required` marks the input required and adds a visible asterisk plus screen-reader text on the label. `error` sets `aria-invalid`. Error text is announced with `aria-live="assertive"`. Warning text uses `aria-live="polite"`.

Arrow Down opens the calendar and moves focus to the first control in the popover. Escape closes the calendar and returns focus to the input. Inside the calendar, arrow keys move by day or week, Page Up and Page Down move by month, and Shift plus those keys moves by year. Enter or Space selects the focused day. Selecting the end date closes the calendar and returns focus to the input.

## Testing

The field's interactive sub-elements carry a stable `data-anv-part` attribute so end-to-end tests can target them directly. The library owns the part names; you supply the instance identifier.

Attach your own `data-*` attribute (such as `data-cy` or `data-testid`) to the field. It is forwarded to the masked text input — the one element present in every mode, including `disableCalendar`:

```tsx theme={null}
<DateFieldRange data-cy="booking-range" label="Booking range" />
```

```ts theme={null}
cy.get('[data-cy="booking-range"]').type("06/15/2025 - 06/20/2025");
```

| `data-anv-part` | Element |
| - | - |
| `date-input` | Masked text input |
| `popover` | Calendar popover container |
| `date-calendar` | Calendar surface inside the popover |

The masked input also exposes the current range for assertions:

| Attribute | Meaning |
| - | - |
| `data-start-date` | Committed start as `YYYY-MM-DD`, or empty |
| `data-end-date` | Committed end as `YYYY-MM-DD`, or empty |
| `data-input-valid` | `"true"` when both ends are complete dates |
| `data-input-empty` | `"true"` when no date digits remain |
| `data-date-range-valid` | `"true"` when the range passes constraints, including a non-required empty range |

> [!NOTE]
>
> The popover and calendar render in the browser's top layer, outside the input's subtree, so a descendant query scoped to your `data-cy` on the input will not reach them. Target them by their part attribute directly (`[data-anv-part="date-calendar"]`) or by the popover's `data-testid="calendar-popover"`. Only one calendar is open at a time.

**DateFieldRange Props**

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

function ExampleComponent() {
  return (
    <DateFieldRange
      label="Reporting period"
      defaultValue={{
        startDate: "1999-12-25",
        endDate: "2000-01-01",
      }}
      mode="mm/dd/yyyy"
      disableHint={false}
      disableCalendar={false}
      required={false}
    />
  );
}
```

## `DateFieldRange` Props

Every prop below is optional. `id`, `name`, and `data-*` attributes are forwarded to the masked input. `className`, `style`, and [layout props](https://anvil.servicetitan.com/docs/utilities/web/layout-props.md) land on the text field element.

The field does not accept `prefix` (the calendar icon is built in), `hint` (the format line replaces it), `showCounter`, `autoComplete` (always `"off"`), `onClick`, or `onKeyDown`.

**defaultValue** `\{ startDate: string | null; endDate: string | null \} | null`

Initial range for uncontrolled use. Pass `null` to start empty. Date-only ISO strings (`YYYY-MM-DD`).

**disableCalendar** `boolean` (default: false)

Renders the masked input without the popover calendar. Arrow Down and click no longer open a calendar.

**disableHint** `boolean` (default: false)

Hides the format hint under the field (`Format: mm/dd/yyyy` or `Format: dd/mm/yyyy`).

**maxDate** `string`

Latest date either end may use. Date-only ISO string. Later dates are disabled in the calendar and fail `isDateRangeValid`.

**minDate** `string`

Earliest date either end may use. Date-only ISO string. Earlier dates are disabled in the calendar and fail `isDateRangeValid`.

**mode** `"mm/dd/yyyy" | "dd/mm/yyyy"` (default: mm/dd/yyyy)

Order of month, day, and year in the mask and the format hint.

**onBlur** `(event: React.FocusEvent) =\> void`

Called when the input loses focus, including when focus moves into the calendar. Not called when `disableCalendar` is set. Leaving the field entirely also closes an open calendar.

**onChange** `(change: DateFieldRangeChange) =\> void`

Called when the parsed range or either validity flag changes. Copy `startDate` and `endDate` into `value`. The full change object is not a `DateFieldRangeValue`.

**onFocus** `(event: React.FocusEvent) =\> void`

Called when the input receives focus. Not called when `disableCalendar` is set.

**required** `boolean` (default: false)

Marks the input required. An empty range then fails `isDateRangeValid`. Does not render an error message by itself.

**timezone** `IanaZone`

IANA time zone for the calendar's Today action and, when the field has no value, the month it opens on. A different zone than the browser's shows "Dates shown in" plus the zone abbreviation. Example: `"America/Los_Angeles"`.

**unavailable** `\{ dates?: string[]; daysOfWeek?: (1 | 2 | 3 | 4 | 5 | 6 | 7)[] \}`

Dates and weekdays that cannot be selected. `dates` are date-only ISO strings. `daysOfWeek` uses Luxon numbers: Monday is `1` and Sunday is `7`. Matching days are disabled in the calendar and fail `isDateRangeValid`.

**value** `\{ startDate: string | null; endDate: string | null \} | null`

Controlled range. Pass `null` to clear both ends. Date-only ISO strings (`YYYY-MM-DD`).

### Additional field props

**description** `ReactElement | string`

Secondary copy under the format hint.

**disabled** `boolean` (default: false)

Prevents interaction and closes an open calendar.

**error** `boolean | string | string[] | ReactElement`

Applies the error style and sets `aria-invalid`. A string or string array is also shown under the field. `true` is the style with no message.

> [!WARNING]
>
> Passing a `ReactElement` is deprecated. Use a string or `string[]`.

**errorAriaLive** `"off" | "polite" | "assertive"`

Deprecated and ignored. Error text always uses `aria-live="assertive"`.

**hideLabel** `boolean` (default: false)

Hides the visible label and uses the label string as the input's `aria-label`. Inline markdown is stripped. Requires `label`.

**isHighlighted** `boolean` (default: false)

Applies a subdued primary background to draw attention to the field.

**label** `ReactNode`

Accessible name for the field. A string supports inline markdown: bold (`**text**`), italic (`*text*`), bold and italic (`***text***`), highlight (`==text==`), and code (`` `text` ``).

> [!WARNING]
>
> Omitting `label` is deprecated and will be required in v4.0.0. Use `hideLabel` to visually hide it. Passing `ReactNode` is also deprecated — use a plain string with inline markdown instead.

**labelProps** `FieldLabelProps`

Props for the [FieldLabel](https://anvil.servicetitan.com/docs/components/field-label/web.md), including [`aiMark`](https://anvil.servicetitan.com/docs/utilities/web/ai-marks). The visible label text still comes from `label`.

**loading** `boolean` (default: false)

Shows a spinner after the input.

**maxLength** `number`

Maximum characters the input accepts. The mask is 23 characters (`mm/dd/yyyy - mm/dd/yyyy`), so a smaller limit blocks a complete range. This field has no character counter.

**moreInfo** `ReactNode`

Tooltip content for the info button after the label.

**placeholder** `string`

Has no visible effect. The mask keeps a value in the field, so the HTML placeholder never appears. Use the format hint or `description` to explain the expected format.

**size** `"small" | "medium" | "large"` (default: medium)

Field height: small is 32px, medium is 40px, and large is 48px.

**suffix** `string | ReactElement`

Content after the input. Hidden from assistive technology.

**type** `"text" | "email" | "tel" | "url" | "password" | "number"` (default: text)

Forwarded to the masked input. Leave it as `"text"` so typed dates reach the mask. Other values block normal date entry.

**warning** `string | string[]`

Warning copy under the field, announced with `aria-live="polite"`.

**DateFieldRangeChange**

## DateFieldRangeChange

`onChange` receives this object. It is not the controlled `value`.

**endDate** `string | null`

Last committed end as a date-only ISO string, such as `"2025-07-02"`, or `null` when that end is empty or incomplete.

**isDateRangeValid** `boolean`

Whether the last committed dates satisfy `required`, unavailable dates, unavailable weekdays, and `minDate` / `maxDate`, and the start is not after the end. Independent of `isInputValid`. An empty range is valid unless `required` is set. An incomplete edit can leave this `true` while the text is still unfinished.

**isInputEmpty** `boolean`

Whether no date digits remain after the mask placeholder is removed. The field can look filled with `mm/dd/yyyy - mm/dd/yyyy` and still be empty.

**isInputValid** `boolean`

Whether both ends are complete dates. Independent of constraints: an inverted or unavailable range can still be `true`.

**startDate** `string | null`

Last committed start as a date-only ISO string, such as `"2025-03-22"`, or `null` when the start is empty or incomplete.

**DateFieldRangeValue**

## DateFieldRangeValue

The controlled `value` and uncontrolled `defaultValue` use this type. Pass `null` to clear both ends. An object value has these fields. This is not the object `onChange` receives.

**endDate** `string | null`

End as a date-only ISO string (`YYYY-MM-DD`), or `null` when that end is empty.

**startDate** `string | null`

Start as a date-only ISO string (`YYYY-MM-DD`), or `null` when the start is empty.


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