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

# Interval frequency

> Form.IntervalFrequency pairs a numeric interval with a frequency select, bound to a single IntervalFrequencyFormState.

`Form.IntervalFrequency` pairs a numeric interval input (e.g. "every N") with a frequency/unit select (e.g. "days" / "weeks" / "months") as one bound field. `label` renders as a single visible legend above both inputs — the interval and frequency inputs each have their own fixed, visually hidden accessible name ("Interval" / "Frequency") instead of individual visible labels, since a per-input label reads too small next to the legend. It renders both inputs in a two-column grid with a single `FieldMessage` below them, and binds to an `IntervalFrequencyFormState` rather than a single field state.

## Creating the form state

`IntervalFrequencyFormState<G>` combines an `interval: A2InputFieldState<number | null>` with a `frequency: SelectableOptionsFieldState<G>` into one `formstate.FormState`. Pass the initial interval and frequency to the constructor, then set the frequency options:

```tsx theme={null}
import { Form, IntervalFrequencyFormState } from "@servicetitan/anvil2-ext-common";
import type { SelectFieldOption } from "@servicetitan/anvil2/beta";

const frequencyUnits: SelectFieldOption[] = [
  { id: "days", label: "Days" },
  { id: "weeks", label: "Weeks" },
];

const reminderCadence = new IntervalFrequencyFormState<SelectFieldOption>(1, frequencyUnits[0]);
reminderCadence.$.frequency.setOptions(frequencyUnits);

function ReminderCadence() {
  return (
    <Form.IntervalFrequency
      label="Remind every"
      formState={reminderCadence}
      options={frequencyUnits}
    />
  );
}
```

`Form.IntervalFrequency` does not accept a `loadOptions` prop — `frequency` is always a `SelectableOptionsFieldState`, since a frequency/unit list is a small, static set. `setOptions()` only feeds `frequency`'s `validationRules` tracking; pass the same list through the `options` prop (forwarded to the underlying `SelectFieldSync`) so the select renders it.

## Reading and validating the combined value

`reminderCadence.$.interval` and `reminderCadence.$.frequency` are plain `A2InputFieldState`/`SelectableOptionsFieldState` instances — read `.value`, attach `.validators()`, and call `.validate()` on either sub-field individually, or validate the whole form state at once:

```tsx theme={null}
reminderCadence.$.interval.validators((value) => value == null && "Interval is required");

const result = await reminderCadence.validate();
if (!result.hasError) {
  console.log(result.value.interval, result.value.frequency);
}
```

## Reacting to changes

Register a handler that runs whenever either sub-field changes with `onDidChange`:

```tsx theme={null}
reminderCadence.onDidChange(({ interval, frequency }) => {
  console.log("cadence changed", interval, frequency?.id);
});
```

## Update mode

Call `enableUpdateMode()` to add an `updateMode: A2InputFieldState<UpdateMode>` sub-field for value-update controls (bulk edit flows that need Keep/Replace/Append/Remove/Clear semantics), and `disableUpdateMode()` to remove it.

## Props

`Form.IntervalFrequency` accepts every `SelectFieldSync` prop except `value`, `onSelectedOptionChange`, `error`, and `warning` (those come from `formState`), and `label`/`hideLabel` (the frequency select's own label is fixed and hidden internally — use the `label` prop below for the visible legend instead), plus:

| Prop | Type | Description |
| - | - | - |
| `formState` | `IntervalFrequencyFormState<G>` | Required. The combined form state to bind to. |
| `label` | `string` | Required. Visible legend above the interval/frequency pair, e.g. "Duration". |
| `description` | `string` | Description text rendered below the field message area. |
| `hideControls` | `boolean` | Hides the interval field's stepper buttons without disabling keyboard stepping. Defaults to `false`. |
| `labelAiMark` | `AiMarkWithTooltipOrPopoverProps["aiMark"]` | AI mark rendered after the legend. |
| `moreInfo` | `ReactNode` | Tooltip content for the info button after the legend. |
| `required` | `boolean` | Shows a required indicator on the legend and sets `required` on both inputs. |


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