| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
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.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
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.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
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.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
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).
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
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.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
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 changesnumberOfMonths— Number of months to displaydisabled— Dates that cannot be selectedmin/max— Min/max selection constraintsfooter— Content rendered below the calendarlocale— Intl locale or Kumo locale settingsclassName— 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.