Dates

Range Calendar

Choose a start and end date with keyboard navigation and a continuous range highlight.

Installation

pnpm dlx @mielui/svelte add range-calendar

Usage

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.

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.

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.

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.

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.

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

  • children
    Snippet<[RangeCalendarRootSnippetProps]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • placeholder Bindable
    DateValue

    The placeholder date, used to control the view of the calendar when no value is present.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • disabled
    boolean

    Whether or not the calendar is disabled.

  • value Bindable
    DateRange

    The value of the selected date range.

  • readonly
    boolean

    Whether 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.

  • onValueChange
    OnChangeFn<DateRange>

    A callback function called when the value changes.

  • onPlaceholderChange
    OnChangeFn<DateValue>

    A callback function called when the placeholder value changes.

  • preventDeselect
    boolean

    Whether or not users can deselect a date once selected without selecting another date.

  • minValue
    DateValue

    The minimum date that can be selected in the calendar.

  • maxValue
    DateValue

    The maximum date that can be selected in the calendar.

  • pagedNavigation
    boolean

    Applicable only when numberOfMonths is greater than 1.

  • weekStartsOn
    0 | 1 | 2 | 3 | 4 | 5 | 6

    The 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 weekdays state store should be formatted.

    Default 'short'

  • isDateDisabled
    DateMatcher

    A function that receives a date and returns true or false to indicate whether the date is disabled.

  • isDateUnavailable
    DateMatcher

    Dates 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.

  • fixedWeeks
    boolean

    Display 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 true

  • numberOfMonths
    number

    Determines the number of months to display on the calendar simultaneously. For navigation between months, refer to the pagedNavigation prop.

  • calendarLabel
    string

    This 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.

  • locale
    string

    The default locale setting.

    Default 'en-US'

  • disableDaysOutsideMonth
    boolean

    Whether 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 true will disable this behavior.

    Default true

  • maxDays
    number

    The 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.

  • onStartValueChange
    OnChangeFn<DateValue | undefined>

    A callback function called when the start value changes. This doesn't necessarily mean the value has updated and should be used to apply cosmetic changes to the calendar when only part of the value is changed/completed.

  • onEndValueChange
    OnChangeFn<DateValue | undefined>

    A callback function called when the end value changes. This doesn't necessarily mean the value has updated and should be used to apply cosmetic changes to the calendar when only part of the value is changed/completed.

  • minDays
    number

    The minimum number of days that can be selected in a range.

  • excludeDisabled
    boolean

    Whether 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.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.Grid

  • children
    Snippet<[]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • child
    Snippet<[{ props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.GridBody

  • children
    Snippet<[]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • child
    Snippet<[{ props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.GridHead

  • children
    Snippet<[]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • child
    Snippet<[{ props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.GridRow

  • children
    Snippet<[]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • child
    Snippet<[{ props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.HeadCell

  • children
    Snippet<[]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • child
    Snippet<[{ props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.Header

  • children
    Snippet<[]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • child
    Snippet<[{ props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.Heading

  • children
    Snippet<[CalendarHeadingSnippetProps]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • child
    Snippet<[CalendarHeadingSnippetProps & { props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.MonthSelect

  • children
    Snippet<[CalendarMonthSelectSnippetProps]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • child
    Snippet<[CalendarMonthSelectSnippetProps & { props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • monthFormat
    "numeric" | "long" | "short" | "narrow" | "2-digit" | ((month: number) => string)

    How the month names should be formatted in the select options.

  • months
    number[]

    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.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.NextButton

  • children
    Snippet<[]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • child
    Snippet<[{ props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.PrevButton

  • children
    Snippet<[]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • child
    Snippet<[{ props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.YearSelect

  • children
    Snippet<[CalendarYearSelectSnippetProps]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • child
    Snippet<[CalendarYearSelectSnippetProps & { props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • yearFormat
    "numeric" | "2-digit" | ((year: number) => string)

    How the year should be formatted in the select options.

  • years
    number[]

    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.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.Cell

  • children
    Snippet<[CalendarCellSnippetProps]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • date Required
    CalendarDate | CalendarDateTime | ZonedDateTime

    The date value of the cell.

  • month Required
    CalendarDate | CalendarDateTime | ZonedDateTime

    The month DateValue that this cell is being rendered in.

  • child
    Snippet<[CalendarCellSnippetProps & { props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.Day

  • children
    Snippet<[CalendarDaySnippetProps]>

    Content rendered inside.

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • child
    Snippet<[CalendarDaySnippetProps & { props: Record<string, unknown>; }]>

    Renders your own element in place of the default one. Spread the given props onto it.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

RangeCalendar.Month

  • class
    string

    Classes added to the element.

  • month Required
    { value: DateValue; weeks: DateValue[][]; dates: DateValue[]; }

    Month data from the Root snippet.

  • weekdays Required
    string[]

    Weekday names from the Root snippet.

  • locale
    string

    BCP 47 locale used for names and number formats.

    Default 'en-US'

  • showHeading
    boolean

    Shows the month name above the grid, for layouts with several months.

    Default false

  • day
    Snippet<[date: DateValue]>

    Custom content for each day cell.