Range Calendar
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
Installation
npx @mielui/svelte add range-calendaryarn dlx @mielui/svelte add range-calendarbunx @mielui/svelte add range-calendarUsage
Bind an object with start and end DateValue properties. An incomplete selection has an undefined end. The default view renders Header, PrevButton, Heading, NextButton and Month, the same parts you can compose yourself.
<script lang="ts">
import type { DateValue } from '@internationalized/date';
import * as RangeCalendar from '@mielui/svelte/components/range-calendar';
let value = $state<{
start: DateValue | undefined;
end: DateValue | undefined;
}>({
start: undefined,
end: undefined
});
</script>
<RangeCalendar.Root bind:value calendarLabel="Travel dates" />Use CalendarDate from @internationalized/date for date-only values, or parseDate for ISO date strings. Bind placeholder to control the visible month. An explicit locale and placeholder keep server and client formatting predictable. Avoid converting a date-only selection through a UTC JavaScript Date to store it. Save each endpoint with value.start?.toString() and value.end?.toString().
Examples
Bound range
The line under the calendar prints the start and end dates as they change.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
2026-09-17 โ 2026-09-23
Two months
numberOfMonths shows several months side by side, and pagedNavigation moves the arrows by that many. A range can start in one month and end in the next.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
27 | 28 | 29 | 30 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
Choose a start and end date, across any month.
Composition and accessibility
Root exposes a children snippet with months and weekdays. Keep the default Month, or compose Grid, GridHead, GridBody, GridRow, HeadCell, Cell and Day to restyle individual regions. Month also accepts a day(date) snippet. Header, Heading, PrevButton, NextButton, MonthSelect and YearSelect can be omitted, reordered or replaced with another documented composition.
Styled parts forward native attributes and expose bind:element for their underlying element. Arrow keys move between days, Page Up and Page Down change the visible month, and Enter or Space selects. Focus and selection are distinct. Disabled dates cannot be selected; readonly calendars remain navigable. The today indicator and unavailable strike-through provide cues beyond color.
The standalone calendar does not create a form field. Add a named hidden input when it participates in a form, or use the corresponding date picker for editable fields and form serialization. The calendar grid stays still during navigation; only state feedback changes.
Bounds and custom composition
minDays and maxDays limit a range to two to seven days, counting both ends. A second pick outside that limit starts a new range. minValue and maxValue keep the range in September, so this composition leaves out the navigation buttons.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
Range events
onValueChange runs when the range changes. A range is only complete once both ends are set, so onStartValueChange and onEndValueChange report each end as it is picked, before the range is whole. Use them to react to the first click.
preventDeselect stops a click on a selected end from clearing the range. onPlaceholderChange runs when the visible month changes, and disabled on Root turns the whole calendar off.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
Check-in not picked. Check-out not picked.
Unavailable and disabled dates
isDateDisabled removes a date from play: it cannot be focused or picked. isDateUnavailable is softer. An unavailable date is struck through and can still be focused, so keyboard users can read why it is off limits, and picking it marks the value invalid.
A range can be drawn across disabled dates. Set excludeDisabled to clear the range instead whenever it would contain one, so a booking never spans a closed day.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
Grid, formats and parts
weekStartsOn sets the first column, from 0 for Sunday to 6 for Saturday, and falls back to the locale. weekdayFormat picks narrow, short or long weekday names. fixedWeeks is on by default and always draws six rows so the height never jumps between months. Turn it off to draw only the weeks a month needs. Days from the neighbouring months fill the grid and cannot be selected. Set disableDaysOutsideMonth={false} to make them selectable.
monthFormat and yearFormat on Root control how the heading and the selects name months and years. Each takes an Intl style such as short or 2-digit, or a function that receives the number and returns the text. MonthSelect and YearSelect take the same two props to override the format for their own options, and YearSelect takes years, the exact list of years to offer.
When you compose the calendar yourself, every part except Root and Month accepts child, a snippet that receives the part's props so you can render your own element with them, and style for inline styles. Month takes showHeading to print the month name above its grid, which Root turns on by itself when it shows more than one month.
API reference
RangeCalendar.Root
childrenSnippet<[RangeCalendarRootSnippetProps]>Content rendered inside.
idstringId of the element.
placeholderBindableDateValueThe placeholder date, used to control the view of the calendar when no value is present.
stylestring | StyleProperties | nullInline styles for the element.
disabledbooleanWhether or not the calendar is disabled.
valueBindableDateRangeThe value of the selected date range.
readonlybooleanWhether the calendar is readonly. When true, the user will be able to focus and navigate the calendar, but will not be able to select dates.
onValueChangeOnChangeFn<DateRange>A callback function called when the value changes.
onPlaceholderChangeOnChangeFn<DateValue>A callback function called when the placeholder value changes.
preventDeselectbooleanWhether or not users can deselect a date once selected without selecting another date.
minValueDateValueThe minimum date that can be selected in the calendar.
maxValueDateValueThe maximum date that can be selected in the calendar.
pagedNavigationbooleanApplicable only when
numberOfMonthsis greater than 1.weekStartsOn0 | 1 | 2 | 3 | 4 | 5 | 6The day of the week to start the calendar on, which must be a number between 0 and 6, where 0 is Sunday and 6 is Saturday.
weekdayFormat"long" | "narrow" | "short"How the string representation of the weekdays provided via the
weekdaysstate store should be formatted.Default
'short'isDateDisabledDateMatcherA function that receives a date and returns
trueorfalseto indicate whether the date is disabled.isDateUnavailableDateMatcherDates matching the provided matchers are marked as "unavailable." Unlike disabled dates, users can still focus and select unavailable dates. However, selecting an unavailable date renders the date picker as invalid.
fixedWeeksbooleanDisplay 6 weeks per month, regardless the month's number of weeks. This is useful for displaying a consistent calendar, where the size of the calendar doesn't change month to month.
Default
truenumberOfMonthsnumberDetermines the number of months to display on the calendar simultaneously. For navigation between months, refer to the
pagedNavigationprop.calendarLabelstringThis label is exclusively used for accessibility, remaining hidden from the page. It's read by screen readers when the calendar is opened. The current month and year are automatically appended to the label, so you only need to provide the base label.
localestringThe default locale setting.
Default
'en-US'disableDaysOutsideMonthbooleanWhether to disable the selection of days outside the current month. By default, days outside the current month are rendered to fill the calendar grid, but they are not selectable. Setting this prop to
truewill disable this behavior.Default
truemaxDaysnumberThe maximum number of days that can be selected in a range.
monthFormat"numeric" | "long" | "short" | "narrow" | "2-digit" | ((month: number) => string)The format of the month names in the calendar.
yearFormat"numeric" | "2-digit" | ((year: number) => string)The format of the year names in the calendar.
onStartValueChangeOnChangeFn<DateValue | undefined>A callback function called when the start value changes. This doesn't necessarily mean the
valuehas updated and should be used to apply cosmetic changes to the calendar when only part of the value is changed/completed.onEndValueChangeOnChangeFn<DateValue | undefined>A callback function called when the end value changes. This doesn't necessarily mean the
valuehas updated and should be used to apply cosmetic changes to the calendar when only part of the value is changed/completed.minDaysnumberThe minimum number of days that can be selected in a range.
excludeDisabledbooleanWhether to automatically reset the range if any date within the selected range becomes disabled. When true, the entire range will be cleared if a disabled date is found between the start and end dates.
elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.Grid
childrenSnippet<[]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
childSnippet<[{ props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.GridBody
childrenSnippet<[]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
childSnippet<[{ props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.GridHead
childrenSnippet<[]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
childSnippet<[{ props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.GridRow
childrenSnippet<[]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
childSnippet<[{ props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.HeadCell
childrenSnippet<[]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
childSnippet<[{ props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.Header
childrenSnippet<[]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
childSnippet<[{ props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.Heading
childrenSnippet<[CalendarHeadingSnippetProps]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
childSnippet<[CalendarHeadingSnippetProps & { props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.MonthSelect
childrenSnippet<[CalendarMonthSelectSnippetProps]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
childSnippet<[CalendarMonthSelectSnippetProps & { props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.monthFormat"numeric" | "long" | "short" | "narrow" | "2-digit" | ((month: number) => string)How the month names should be formatted in the select options.
monthsnumber[]An array of month numbers (1-12) to display in the select. This allows for flexible month selection, such as showing only specific months or seasons.
elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.NextButton
childrenSnippet<[]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
childSnippet<[{ props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.PrevButton
childrenSnippet<[]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
childSnippet<[{ props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.YearSelect
childrenSnippet<[CalendarYearSelectSnippetProps]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
childSnippet<[CalendarYearSelectSnippetProps & { props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.yearFormat"numeric" | "2-digit" | ((year: number) => string)How the year should be formatted in the select options.
yearsnumber[]An array of years to display in the select. This allows for complete control over which years appear, such as showing only years with data or excluding certain periods.
elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.Cell
childrenSnippet<[CalendarCellSnippetProps]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
dateRequiredCalendarDate | CalendarDateTime | ZonedDateTimeThe date value of the cell.
monthRequiredCalendarDate | CalendarDateTime | ZonedDateTimeThe month DateValue that this cell is being rendered in.
childSnippet<[CalendarCellSnippetProps & { props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.Day
childrenSnippet<[CalendarDaySnippetProps]>Content rendered inside.
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
childSnippet<[CalendarDaySnippetProps & { props: Record<string, unknown>; }]>Renders your own element in place of the default one. Spread the given
propsonto it.elementBindableHTMLElement | nullBindable reference to the DOM element.
Default
null
RangeCalendar.Month
classstringClasses added to the element.
monthRequired{ value: DateValue; weeks: DateValue[][]; dates: DateValue[]; }Month data from the Root snippet.
weekdaysRequiredstring[]Weekday names from the Root snippet.
localestringBCP 47 locale used for names and number formats.
Default
'en-US'showHeadingbooleanShows the month name above the grid, for layouts with several months.
Default
falsedaySnippet<[date: DateValue]>Custom content for each day cell.
pnpm dlx @mielui/svelte add range-calendar