Skip to main content
Anvil2 date field components use ISO 8601 date strings for input and output. This page explains the string format, why Anvil2 uses strings instead of JavaScript Date objects, and how to convert values from your application’s date library.

String format

Date field components accept and return date-only ISO strings in YYYY-MM-DD format (for example, "2025-07-02"). Full ISO datetime strings (for example, "2025-07-02T00:00:00.000Z") are also accepted as input. Date fields normalize these values to the calendar date and emit date-only strings from onChange callbacks.
DirectionFormatExample
Input (value, defaultValue, minDate, maxDate)ISO 8601 date string"2025-07-02"
Output (onChange)ISO 8601 date string or null"2025-07-02" or null
Range outputObject with startDate and endDate strings or null{ startDate: "2025-07-01", endDate: "2025-07-31" }
Date Field Yearless and Date Field Yearless Range use { month, day } objects instead of ISO strings because they represent recurring dates without a year. See Date Field Yearless and Date Field Yearless Range for those APIs.Calendar accepts date-only strings as input but returns full ISO datetime strings from onSelection (for example, “2025-07-02T00:00:00.000-07:00”). Convert Calendar output with value?.split("T")[0] (handle undefined) or a date library before passing it to date fields.

Why strings instead of JavaScript Date objects

Anvil2 date components use strings rather than JavaScript Date objects for three reasons:
  1. Library neutrality — Teams across ServiceTitan use different date libraries (Luxon, Moment, Dispatch Temporal-lite, and others). ISO strings provide a single interchange format that any library can parse and produce without coupling components to one runtime type.
  2. Predictable calendar dates — JavaScript Date objects carry timezone and time-of-day information. Passing a Date between layers can shift the displayed calendar day. Date-only strings represent the intended calendar date directly.
  3. API alignment — Backend services and generated API clients often serialize dates as ISO strings. Strings map directly to form state, request payloads, and URL parameters without intermediate conversion at the component boundary.
Anvil1 Date Picker components accepted JavaScript Date objects. When migrating to Anvil2, convert Date values to ISO strings before passing them to date fields. See the Anvil to Anvil2 migration guide for conversion patterns.

Components that use date strings

The following Anvil2 components use ISO date strings:
ComponentValue propOutput
Date Field Singlestring | nullchange.date (date-only)
Date Field Range{ startDate: string | null; endDate: string | null } | nullchange.startDate, change.endDate (date-only)
Calendarstring or { start, end } (date-only input)onSelection (full ISO datetime)
Filter Bar date filtersISO string, range object, or nullFilter value

Convert from JavaScript Date

When your application stores dates as JavaScript Date objects, convert to a date-only string before passing the value to date fields. If your API client already returns ISO date strings, pass them directly without conversion.
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}`;
}

// Usage with DateFieldSingle
<DateFieldSingle
  value={apiResponse.scheduledDate ? toDateString(apiResponse.scheduledDate) : null}
  onChange={(change) => {
    if (change.date) {
      setScheduledDate(change.date);
    }
  }}
/>
Use local date components (getFullYear, getMonth, getDate) rather than toISOString() when converting from a JavaScript Date. The toISOString() method returns UTC, which can produce a different calendar date in some timezones.

Convert from Luxon

Anvil2 uses Luxon internally for date parsing. If your application already uses Luxon, use toISODate():
import { DateTime } from "luxon";
import { DateFieldSingle } from "@servicetitan/anvil2";

const today = DateTime.now().toISODate(); // "2025-07-02"

<DateFieldSingle
  value={today}
  onChange={(change) => {
    if (change.date) {
      setSelectedDate(DateTime.fromISO(change.date, { zone: "utc" }));
    } else {
      setSelectedDate(null);
    }
  }}
/>

Convert from other date libraries

The same ISO string format works with any date library:
import moment from "moment";
// Moment.js
const momentDateString = moment().format("YYYY-MM-DD");
// date-fns
import { format } from "date-fns";
const dateFnsDateString = format(new Date(), "yyyy-MM-dd");
Parse Anvil2 output with your library’s ISO parser, then store or transmit the value in whatever format your application layer requires.

Controlled usage pattern

Store ISO date strings in React state and pass them directly to date components:
import { DateFieldSingle, DateFieldRange } from "@servicetitan/anvil2";
import { useState } from "react";

function AppointmentForm() {
  const [appointmentDate, setAppointmentDate] = useState<string | null>(null);
  const [reportRange, setReportRange] = useState<{
    startDate: string | null;
    endDate: string | null;
  }>({ startDate: null, endDate: null });

  return (
    <>
      <DateFieldSingle
        label="Appointment date"
        value={appointmentDate}
        onChange={(change) => setAppointmentDate(change.date)}
      />
      <DateFieldRange
        label="Report period"
        value={reportRange}
        onChange={(change) =>
          setReportRange({
            startDate: change.startDate,
            endDate: change.endDate,
          })
        }
      />
    </>
  );
}
Submit form data by passing the ISO strings directly to your API, or convert them to your backend’s expected format in the submit handler.
Last modified on June 22, 2026