July 2026
SunMonTueWedThuFriSat

Selected: None

import { createSignal } from "solid-js";
import { DatePicker } from "@photon-ai/kumo-solid";

/**
 * Single date selection.
 */
export function DatePickerSingleDemo() {
  const [date, setDate] = createSignal<Date | undefined>();

  return (
    <div class="flex flex-col gap-4">
      <DatePicker
        mode="single"
        selected={date()}
        onChange={(d) => {
          if (d) {
            setDate(d);
          }
        }}
      />
      <p class="text-sm text-kumo-subtle">
        Selected: {date()?.toLocaleDateString() ?? "None"}
      </p>
    </div>
  );
}

Installation

Barrel

import { DatePicker, type DateRange } from "@photon-ai/kumo-solid";

Granular

import {
  DatePicker,
  type DateRange,
} from "@photon-ai/kumo-solid/components/date-picker";

Usage

DatePicker supports three selection modes: single, multiple, and range.

import { createSignal } from "solid-js";
import { DatePicker } from "@photon-ai/kumo-solid";

export default function Example() {
  const [date, setDate] = createSignal<Date>();

  return <DatePicker mode="single" selected={date()} onChange={setDate} />;
}

Examples

Single Date Selection

Select a single date. The most common use case for date pickers.

July 2026
SunMonTueWedThuFriSat

Selected: None

import { createSignal } from "solid-js";
import { DatePicker } from "@photon-ai/kumo-solid";

/**
 * Single date selection.
 */
export function DatePickerSingleDemo() {
  const [date, setDate] = createSignal<Date | undefined>();

  return (
    <div class="flex flex-col gap-4">
      <DatePicker
        mode="single"
        selected={date()}
        onChange={(d) => {
          if (d) {
            setDate(d);
          }
        }}
      />
      <p class="text-sm text-kumo-subtle">
        Selected: {date()?.toLocaleDateString() ?? "None"}
      </p>
    </div>
  );
}

Multiple Date Selection

Select multiple individual dates. Use max to limit the number of selections.

July 2026
SunMonTueWedThuFriSat

Selected: 0 date(s)

import { createSignal } from "solid-js";
import { DatePicker } from "@photon-ai/kumo-solid";

/**
 * Multiple date selection with a maximum of 5 dates.
 */
export function DatePickerMultipleDemo() {
  const [dates, setDates] = createSignal<Date[] | undefined>();

  return (
    <div class="flex flex-col gap-4">
      <DatePicker
        mode="multiple"
        selected={dates()}
        onChange={setDates}
        max={5}
      />
      <p class="text-sm text-kumo-subtle">
        Selected: {dates()?.length ?? 0} date(s)
      </p>
    </div>
  );
}

Date Range Selection

Select a continuous range of dates. Works well with numberOfMonths={2} for a side-by-side view.

July 2026
SunMonTueWedThuFriSat
August 2026
SunMonTueWedThuFriSat

Range: None

import { createSignal } from "solid-js";
import { DatePicker, DateRange } from "@photon-ai/kumo-solid";

/**
 * Date range selection with two months displayed.
 */
export function DatePickerRangeDemo() {
  const [range, setRange] = createSignal<DateRange | undefined>();
  const formattedRange = () => {
    const current = range();
    if (!current?.from) return "None";
    return `${current.from.toLocaleDateString()} - ${current.to?.toLocaleDateString() ?? "..."}`;
  };

  return (
    <div class="flex flex-col gap-4">
      <DatePicker
        mode="range"
        selected={range()}
        onChange={setRange}
        numberOfMonths={2}
      />
      <p class="text-sm text-kumo-subtle">Range: {formattedRange()}</p>
    </div>
  );
}

Range with Min/Max Constraints

Constrain the range length using min and max props (in days/nights).

July 2026
SunMonTueWedThuFriSat
import { createSignal } from "solid-js";
import { DatePicker, DateRange } from "@photon-ai/kumo-solid";

/**
 * Date range with minimum 3 nights and maximum 7 nights.
 */
export function DatePickerRangeMinMaxDemo() {
  const [range, setRange] = createSignal<DateRange | undefined>();

  return (
    <div class="flex flex-col gap-4">
      <DatePicker
        mode="range"
        selected={range()}
        onChange={setRange}
        min={3}
        max={7}
        footer={<span class="text-xs text-kumo-subtle">Select 3-7 nights</span>}
      />
    </div>
  );
}

With Popover

Compose with the Popover component to create a dropdown date picker.

import { createSignal } from "solid-js";
import { DatePicker, Popover, Button } from "@photon-ai/kumo-solid";
import { CalendarDotsIcon } from "~/components/icons";

/**
 * Date picker composed with a Popover for dropdown behavior.
 */
export function DatePickerPopoverDemo() {
  const [date, setDate] = createSignal<Date | undefined>();

  return (
    <Popover>
      <Popover.Trigger
        render={(renderProps) => (
          <Button {...renderProps} variant="outline" icon={CalendarDotsIcon} />
        )}
      >
        {date()?.toLocaleDateString() ?? "Pick a date"}
      </Popover.Trigger>
      <Popover.Content className="p-3">
        <DatePicker mode="single" selected={date()} onChange={setDate} />
      </Popover.Content>
    </Popover>
  );
}

Date Range with Popover

A date range picker in a popover with two months displayed.

import { createSignal } from "solid-js";
import { DatePicker, Popover, Button, DateRange } from "@photon-ai/kumo-solid";
import { CalendarDotsIcon } from "~/components/icons";

/**
 * Date range picker composed with a Popover for dropdown behavior.
 */
export function DatePickerRangePopoverDemo() {
  const [range, setRange] = createSignal<DateRange | undefined>();

  const formatRange = () => {
    const current = range();
    if (!current?.from) return "Select dates";
    if (!current.to) return current.from.toLocaleDateString();
    return `${current.from.toLocaleDateString()} – ${current.to.toLocaleDateString()}`;
  };

  return (
    <Popover>
      <Popover.Trigger
        render={(renderProps) => (
          <Button {...renderProps} variant="outline" icon={CalendarDotsIcon} />
        )}
      >
        {formatRange()}
      </Popover.Trigger>
      <Popover.Content className="p-3">
        <DatePicker
          mode="range"
          selected={range()}
          onChange={setRange}
          numberOfMonths={2}
        />
      </Popover.Content>
    </Popover>
  );
}

Date Range with Presets

Combine the date picker with preset options for quick selection.

import { createSignal } from "solid-js";
import { DatePicker, Popover, Button, DateRange } from "@photon-ai/kumo-solid";
import { CalendarDotsIcon } from "~/components/icons";

/**
 * Date range picker with preset options in a popover.
 */
export function DatePickerRangeWithPresetsDemo() {
  const [range, setRange] = createSignal<DateRange | undefined>();
  const [month, setMonth] = createSignal<Date>(new Date());

  const today = new Date();

  const presets = [
    {
      label: "Today",
      range: { from: today, to: today },
    },
    {
      label: "Last 7 days",
      range: {
        from: new Date(today.getTime() - 6 * 24 * 60 * 60 * 1000),
        to: today,
      },
    },
    {
      label: "Last 30 days",
      range: {
        from: new Date(today.getTime() - 29 * 24 * 60 * 60 * 1000),
        to: today,
      },
    },
    {
      label: "Last 90 days",
      range: {
        from: new Date(today.getTime() - 89 * 24 * 60 * 60 * 1000),
        to: today,
      },
    },
    {
      label: "This month",
      range: {
        from: new Date(today.getFullYear(), today.getMonth(), 1),
        to: new Date(today.getFullYear(), today.getMonth() + 1, 0),
      },
    },
    {
      label: "Last month",
      range: {
        from: new Date(today.getFullYear(), today.getMonth() - 1, 1),
        to: new Date(today.getFullYear(), today.getMonth(), 0),
      },
    },
  ];

  const handlePresetClick = (preset: { range: DateRange }) => {
    setRange(preset.range);
    // Navigate calendar to show the start of the range
    if (preset.range.from) {
      setMonth(preset.range.from);
    }
  };

  const isPresetActive = (preset: { range: DateRange }) => {
    const current = range();
    if (
      !current?.from ||
      !current.to ||
      !preset.range.from ||
      !preset.range.to
    ) {
      return false;
    }
    // Compare dates only (ignore time)
    const sameFrom =
      current.from.toDateString() === preset.range.from.toDateString();
    const sameTo = current.to.toDateString() === preset.range.to.toDateString();
    return sameFrom && sameTo;
  };

  const formatRange = () => {
    const current = range();
    if (!current?.from) return "Select dates";
    if (!current.to) return current.from.toLocaleDateString();
    return `${current.from.toLocaleDateString()} – ${current.to.toLocaleDateString()}`;
  };

  return (
    <Popover>
      <Popover.Trigger
        render={(renderProps) => (
          <Button {...renderProps} variant="outline" icon={CalendarDotsIcon} />
        )}
      >
        {formatRange()}
      </Popover.Trigger>
      <Popover.Content className="p-0">
        <div class="flex">
          <div class="flex flex-col gap-1 border-r border-kumo-hairline p-2 text-sm">
            {presets.map((preset) => {
              const isActive = isPresetActive(preset);
              return (
                <button
                  type="button"
                  onClick={() => handlePresetClick(preset)}
                  class={`rounded-md px-3 py-1.5 text-left whitespace-nowrap ${
                    isActive
                      ? "bg-kumo-bg-inverse text-kumo-text-inverse"
                      : "text-kumo-subtle hover:bg-kumo-control"
                  }`}
                >
                  {preset.label}
                </button>
              );
            })}
          </div>
          <div class="p-3">
            <DatePicker
              mode="range"
              selected={range()}
              onChange={setRange}
              month={month()}
              onMonthChange={setMonth}
              numberOfMonths={2}
            />
          </div>
        </div>
      </Popover.Content>
    </Popover>
  );
}

Disabled Dates with Usage Limits

Use the disabled prop to make certain dates unselectable, and footer to display usage information.

July 2026
SunMonTueWedThuFriSat
import { createSignal } from "solid-js";
import { DatePicker } from "@photon-ai/kumo-solid";

/**
 * Date picker with disabled dates and a footer showing usage limits.
 */
export function DatePickerDisabledWithFooterDemo() {
  const [dates, setDates] = createSignal<Date[] | undefined>();
  const today = new Date();

  // Example: some dates are already used/unavailable
  const unavailableDates = [
    new Date(today.getFullYear(), today.getMonth(), 5),
    new Date(today.getFullYear(), today.getMonth(), 12),
    new Date(today.getFullYear(), today.getMonth(), 18),
    new Date(today.getFullYear(), today.getMonth(), 25),
  ];

  const selectedCount = () => dates()?.length ?? 0;
  const maxDays = 5;

  return (
    <DatePicker
      mode="multiple"
      selected={dates()}
      onChange={setDates}
      max={maxDays}
      disabled={unavailableDates}
      fixedWeeks
      footer={
        <p class="w-full pt-2 text-xs text-kumo-subtle">
          {selectedCount()}/{maxDays} days selected. Grayed dates are
          unavailable.
        </p>
      }
    />
  );
}

Full Popover Example

Here’s a complete example showing how to compose DatePicker with Popover:

import { createSignal } from "solid-js";
import { DatePicker, Popover, Button } from "@photon-ai/kumo-solid";
import { CalendarDotsIcon } from "~/components/icons";

export function DatePickerDropdown() {
  const [date, setDate] = createSignal<Date>();

  return (
    <Popover>
      <Popover.Trigger
        render={(triggerProps) => (
          <Button {...triggerProps} variant="outline" icon={CalendarDotsIcon} />
        )}
      >
        {date()?.toLocaleDateString() ?? "Pick a date"}
      </Popover.Trigger>
      <Popover.Content class="p-3">
        <DatePicker
          mode="single"
          selected={date()}
          onChange={setDate}
        />
      </Popover.Content>
    </Popover>
  );
}

API Reference

DatePicker provides a Solid-native, type-safe API inspired by

DayPicker

. It is implemented natively for Solid and does not ship react-day-picker. Key props include:

  • mode"single" | "multiple" | "range" — Selection mode (required)

  • selected — Currently selected date(s)
  • onChange — Callback when selection changes
  • numberOfMonths — Number of months to display
  • disabled — Dates that cannot be selected
  • min / max — Min/max selection constraints
  • footer — Content rendered below the calendar
  • locale — Intl locale or Kumo locale settings
  • className — Additional CSS classes

The

DayPicker documentation

is useful background for the familiar concepts, while the Kumo prop types remain the source of truth.

Differences from DayPicker

For consistency with other Kumo form components, DatePicker uses onChange instead of DayPicker’s onSelect. Full type inference is preserved — TypeScript will correctly narrow the callback signature based on the mode prop.