---
title: Date Picker
description: "A date selection calendar supporting single, multiple, and range selection modes."
order: 11
tags: [components]
---

import { Preview } from "$lib/components/preview";
import { Button, DatePicker, Icon, Popover } from "areia";
import { Calendar } from "lucide";

# Date Picker

A date selection calendar supporting single, multiple, and range selection modes.

[Source Code](https://github.com/ilhajs/areia/blob/main/packages/areia/src/components/date-picker/index.ts)

Areia's `DatePicker` uses `date-fns` for month generation and renders a vanilla Tailwind calendar. Use `DatePicker` when you want an interactive Ilha island; it updates its own calendar UI and emits date-picker events.

<Preview
  code={
    'import ilha from "ilha";\nimport { DatePicker } from "areia";\n\nexport default ilha.render(() => (\n  <DatePicker\n    mode="single"\n    selected={new Date(2026, 4, 21)}\n    defaultMonth={new Date(2026, 4, 1)}\n  />\n));'
  }
  lang="tsx"
  codeOnly
/>

## Import

```ts
import { DatePicker, type DateRange } from "areia";
```

## Usage

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

<Preview
  code={
    'import ilha from "ilha";\nimport { DatePicker } from "areia";\n\nexport default ilha.render(() => <DatePicker mode="single" />);'
  }
  lang="tsx"
>
  <DatePicker mode="single" />
</Preview>

Call `DatePicker(...)` for interactive behavior. It dispatches `date-picker:change` when selection changes and `date-picker:month-change` when users navigate months.

Use `DatePicker.Static(...)` for static markup or when you want to initialize/wire behavior yourself. For most application usage, call `DatePicker(...)`.

## Examples

### Single Date Selection

Select a single date. This is the most common use case for date pickers.

<Preview
  code={
    'import ilha from "ilha";\nimport { DatePicker } from "areia";\n\nexport default ilha.render(() => (\n  <div class="flex flex-col gap-3">\n    <DatePicker\n      mode="single"\n      selected={new Date(2026, 4, 21)}\n      defaultMonth={new Date(2026, 4, 1)}\n    />\n    <p class="text-sm text-areia-subtle">\n      Selected: May 21, 2026\n    </p>\n  </div>\n));'
  }
  lang="tsx"
  codeOnly
/>

### Multiple Date Selection

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

<Preview
  code={
    'import ilha from "ilha";\nimport { DatePicker } from "areia";\n\nexport default ilha.render(() => (\n  <div class="flex flex-col gap-3">\n    <DatePicker\n      mode="multiple"\n      selected={[\n        new Date(2026, 4, 7),\n        new Date(2026, 4, 14),\n        new Date(2026, 4, 21),\n      ]}\n      defaultMonth={new Date(2026, 4, 1)}\n      maxSelected={5}\n    />\n    <p class="text-sm text-areia-subtle">\n      Select up to 5 dates.\n    </p>\n  </div>\n));'
  }
  lang="tsx"
  codeOnly
/>

### Date Range Selection

Select a continuous date range. Range selection works well with `numberOfMonths: 2` for a side-by-side view.

<Preview
  code={
    'import ilha from "ilha";\nimport { DatePicker, type DateRange } from "areia";\n\nconst range: DateRange = {\n  from: new Date(2026, 4, 12),\n  to: new Date(2026, 4, 18),\n};\n\nexport default ilha.render(() => (\n  <DatePicker\n    mode="range"\n    selected={range}\n    defaultMonth={new Date(2026, 4, 1)}\n    numberOfMonths={2}\n  />\n));'
  }
  lang="tsx"
  codeOnly
/>

### Min and Max Dates

Use `min` and `max` to disable dates before or after an allowed range.

<Preview
  code={
    'import ilha from "ilha";\nimport { DatePicker } from "areia";\n\nexport default ilha.render(() => (\n  <div class="flex flex-col gap-3">\n    <DatePicker\n      mode="single"\n      defaultMonth={new Date(2026, 4, 1)}\n      min={new Date(2026, 4, 5)}\n      max={new Date(2026, 4, 25)}\n    />\n    <p class="text-sm text-areia-subtle">\n      Only dates from May 5–25 are selectable.\n    </p>\n  </div>\n));'
  }
  lang="tsx"
  codeOnly
/>

### Disabled Dates

Use `disabled` to make specific dates unavailable.

<Preview
  code={
    'import ilha from "ilha";\nimport { DatePicker } from "areia";\n\nexport default ilha.render(() => (\n  <DatePicker\n    mode="multiple"\n    defaultMonth={new Date(2026, 4, 1)}\n    disabled={[\n      new Date(2026, 4, 5),\n      new Date(2026, 4, 12),\n      new Date(2026, 4, 18),\n      new Date(2026, 4, 25),\n    ]}\n    maxSelected={5}\n  />\n));'
  }
  lang="tsx"
  codeOnly
/>

### Custom Week Start

Use `weekStartsOn` to choose the first day of the week. `0` is Sunday and `1` is Monday.

<Preview
  code={
    'import ilha from "ilha";\nimport { DatePicker } from "areia";\n\nexport default ilha.render(() => (\n  <DatePicker\n    mode="single"\n    defaultMonth={new Date(2026, 4, 1)}\n    weekStartsOn={1}\n  />\n));'
  }
  lang="tsx"
  codeOnly
/>

### Without Outside Days

Set `showOutsideDays: false` to hide adjacent-month dates in the calendar grid.

<Preview
  code={
    'import ilha from "ilha";\nimport { DatePicker } from "areia";\n\nexport default ilha.render(() => (\n  <DatePicker\n    mode="single"\n    defaultMonth={new Date(2026, 4, 1)}\n    showOutsideDays={false}\n  />\n));'
  }
  lang="tsx"
  codeOnly
/>

### With Popover

Compose with [Popover](/components/popover) to create a dropdown date picker.

<Preview
  code={
    'import ilha from "ilha";\nimport { Calendar } from "lucide";\nimport { Button, DatePicker, Icon, Popover } from "areia";\n\nexport default ilha.render(() => (\n  <Popover\n    trigger={\n      <Button icon={<Icon icon={Calendar} />}>\n        Pick a date\n      </Button>\n    }\n    content={\n      <DatePicker\n        mode="single"\n        defaultMonth={new Date(2026, 4, 1)}\n      />\n    }\n    contentClass="p-0"\n  />\n));'
  }
  lang="tsx"
  codeOnly
/>

### Range with Popover

Render two months inside a popover for a compact date-range picker.

<Preview
  code={
    'import ilha from "ilha";\nimport { Calendar } from "lucide";\nimport {\n  Button,\n  DatePicker,\n  Icon,\n  Popover,\n  type DateRange,\n} from "areia";\n\nconst range: DateRange = {\n  from: new Date(2026, 4, 12),\n  to: new Date(2026, 4, 18),\n};\n\nexport default ilha.render(() => (\n  <Popover\n    trigger={\n      <Button icon={<Icon icon={Calendar} />}>\n        May 12 – May 18\n      </Button>\n    }\n    content={\n      <DatePicker\n        mode="range"\n        selected={range}\n        defaultMonth={new Date(2026, 4, 1)}\n        numberOfMonths={2}\n      />\n    }\n    contentClass="p-0"\n  />\n));'
  }
  lang="tsx"
  codeOnly
/>

### Listening for Changes

`DatePicker` emits bubbling custom events so you can integrate it with application state.

```ts
const picker = document.querySelector(
  '[data-slot="date-picker"]',
);

picker?.addEventListener("date-picker:change", (event) => {
  const { selected } = (event as CustomEvent).detail;
  console.log(selected);
});

picker?.addEventListener(
  "date-picker:month-change",
  (event) => {
    const { month } = (event as CustomEvent).detail;
    console.log(month);
  },
);
```

## API Reference

### DatePicker

Extends native `div` attributes.

| Prop              | Type                                       | Default    | Description                                               |
| ----------------- | ------------------------------------------ | ---------- | --------------------------------------------------------- |
| `mode`            | `"single" \| "multiple" \| "range"`        | `"single"` | Selection mode.                                           |
| `selected`        | `Date \| Date[] \| DateRange \| undefined` | -          | Selected date, dates, or range depending on `mode`.       |
| `month`           | `Date`                                     | -          | Controlled first visible month.                           |
| `defaultMonth`    | `Date`                                     | -          | Initial month when uncontrolled.                          |
| `numberOfMonths`  | `number`                                   | `1`        | Number of consecutive months to render.                   |
| `weekStartsOn`    | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6`          | `0`        | First day of the week.                                    |
| `showOutsideDays` | `boolean`                                  | `true`     | Shows adjacent-month days to fill the calendar grid.      |
| `min`             | `Date`                                     | -          | Disables dates before this date.                          |
| `max`             | `Date`                                     | -          | Disables dates after this date.                           |
| `disabled`        | `Date[] \| ((date: Date) => boolean)`      | -          | Dates that cannot be selected.                            |
| `maxSelected`     | `number`                                   | -          | Maximum number of selected dates in `multiple` mode.      |
| `onChange`        | `(selected: DatePickerSelected) => void`   | -          | Called by `DatePicker` when selection changes.            |
| `onMonthChange`   | `(month: Date) => void`                    | -          | Called by `DatePicker` when month navigation changes.     |
| `class`           | `string`                                   | -          | Additional CSS classes merged with the generated classes. |
| `className`       | `string`                                   | -          | Alias for `class`.                                        |

### DatePicker

`DatePicker` is an Ilha island. It renders the same calendar markup as `DatePicker(...)`, then wires click handlers for month navigation and date selection.

It emits:

| Event                      | Detail            | Description                           |
| -------------------------- | ----------------- | ------------------------------------- |
| `date-picker:change`       | `{ selected }`    | Fired after a date selection changes. |
| `date-picker:month-change` | `{ month: Date }` | Fired after month navigation.         |

### DateRange

```ts
import type { DateRange } from "areia";

const range: DateRange = {
  from: new Date(2026, 4, 12),
  to: new Date(2026, 4, 18),
};
```

## Design Guidelines

### Choosing a Mode

- **single**: Birthdays, deadlines, appointments, and one-off dates.
- **multiple**: Selecting non-contiguous dates like available days or exception dates.
- **range**: Travel, reports, billing periods, and other continuous spans.

### Popover Composition

- Use a popover for compact forms where the calendar should not always be visible.
- Use two months for range selection when horizontal space allows.
- Keep trigger labels clear, such as “Pick a date” or the formatted selected range.

### Accessibility

- Calendar days are rendered as buttons and are keyboard focusable.
- Selected days use `aria-pressed="true"`.
- Disabled days use the native `disabled` attribute.
- Provide surrounding form labels or descriptive text when the picker is part of a larger form.
