Range Calendar
A date range selection calendar with start/end selection, hover preview, and keyboard navigation.
bun x bosia@latest add range-calendarA composable calendar for selecting date ranges. Built on top of the calendar component — reuses CalendarHeader for navigation. Native JS Date, zero external dependencies.
Preview
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Props
RangeCalendar
| Prop | Type | Default |
|---|---|---|
value |
{ start?: Date; end?: Date } |
{ start: undefined, end: undefined } |
month |
Date | undefined |
undefined |
min |
Date | undefined |
undefined |
max |
Date | undefined |
undefined |
disabled |
((date: Date) => boolean) | undefined |
undefined |
weekStartsOn |
0 | 1 | 2 | 3 | 4 | 5 | 6 |
0 |
fixedWeeks |
boolean |
false |
value— bindable range object withstartandenddatesmonth— bindable controlled viewed monthmin/max— earliest/latest selectable datesdisabled— custom function to disable specific datesweekStartsOn— first day of week (0= Sunday,1= Monday, etc.)fixedWeeks— always render 6 rows (42 cells)
Sub-components
RangeCalendar— root wrapper, holds range state and provides dual contextRangeCalendarHeader— re-exportedCalendarHeaderwith prev/next and month/year selectsRangeCalendarGrid—<table>with day-of-week headers and range-aware date cellsRangeCalendarDay— individual day button with range styling (used internally byRangeCalendarGrid)
Selection Behavior
- Click — sets start date
- Click again (on or after start) — sets end date
- Click before start — resets and sets new start
- Click when both set — resets range, sets new start
While selecting (start set, no end), hovering shows a preview range with lighter styling.
Usage
<script lang="ts">
import { RangeCalendar, RangeCalendarGrid } from "$lib/components/ui/range-calendar";
import { CalendarHeader } from "$lib/components/ui/calendar";
let value = $state<{ start?: Date; end?: Date }>({ start: undefined, end: undefined });
</script>
<RangeCalendar bind:value class="rounded-md border">
<CalendarHeader />
<RangeCalendarGrid />
</RangeCalendar>Min / Max Constraints
<RangeCalendar
bind:value
min={new Date(2026, 0, 1)}
max={new Date(2026, 11, 31)}
class="rounded-md border"
>
<CalendarHeader />
<RangeCalendarGrid />
</RangeCalendar>Disable Weekends
<RangeCalendar
bind:value
disabled={(date) => date.getDay() === 0 || date.getDay() === 6}
class="rounded-md border"
>
<CalendarHeader />
<RangeCalendarGrid />
</RangeCalendar>Keyboard Navigation
| Key | Action |
|---|---|
ArrowLeft / ArrowRight |
Previous / next day |
ArrowUp / ArrowDown |
Previous / next week |
Home / End |
First / last day of week |
PageUp / PageDown |
Previous / next month |
Enter / Space |
Select focused day |