# Range Calendar

Composable date-range calendar with shared calendar parts and range highlighting.

- Package: `@mielui/svelte`
- Component version: `2.0.0`
- Depends on Mielui components: _internal/utils, calendar
- Shared utilities: utils.cn

## Install

~~~~sh
pnpm dlx @mielui/svelte add range-calendar
~~~~

## API

This reference is generated at build time from the component manifest, public `index.ts`, and documentation examples below. Changes to those source files are reflected here in the published Markdown. Standard Svelte and HTML attributes accepted by the exported prop types are supported.


### Grid

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| child | Snippet<[{ props: Record<string, unknown>; }]> \| undefined | - | No | No | Renders your own element in place of the default one. Spread the given `props` onto it. |
| element | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### GridBody

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| child | Snippet<[{ props: Record<string, unknown>; }]> \| undefined | - | No | No | Renders your own element in place of the default one. Spread the given `props` onto it. |
| element | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### GridHead

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| child | Snippet<[{ props: Record<string, unknown>; }]> \| undefined | - | No | No | Renders your own element in place of the default one. Spread the given `props` onto it. |
| element | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### GridRow

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| child | Snippet<[{ props: Record<string, unknown>; }]> \| undefined | - | No | No | Renders your own element in place of the default one. Spread the given `props` onto it. |
| element | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### HeadCell

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| child | Snippet<[{ props: Record<string, unknown>; }]> \| undefined | - | No | No | Renders your own element in place of the default one. Spread the given `props` onto it. |
| element | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Header

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| child | Snippet<[{ props: Record<string, unknown>; }]> \| undefined | - | No | No | Renders your own element in place of the default one. Spread the given `props` onto it. |
| element | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Heading

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[CalendarHeadingSnippetProps]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| child | Snippet<[CalendarHeadingSnippetProps & { props: Record<string, unknown>; }]> \| undefined | - | No | No | Renders your own element in place of the default one. Spread the given `props` onto it. |
| element | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### MonthSelect

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[CalendarMonthSelectSnippetProps]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| child | Snippet<[CalendarMonthSelectSnippetProps & { props: Record<string, unknown>; }]> \| undefined | - | No | No | 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) \| undefined | - | No | No | How the month names should be formatted in the select options. |
| months | number[] \| undefined | - | No | No | 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 | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### NextButton

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| child | Snippet<[{ props: Record<string, unknown>; }]> \| undefined | - | No | No | Renders your own element in place of the default one. Spread the given `props` onto it. |
| element | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### PrevButton

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| child | Snippet<[{ props: Record<string, unknown>; }]> \| undefined | - | No | No | Renders your own element in place of the default one. Spread the given `props` onto it. |
| element | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### YearSelect

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[CalendarYearSelectSnippetProps]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| child | Snippet<[CalendarYearSelectSnippetProps & { props: Record<string, unknown>; }]> \| undefined | - | No | No | Renders your own element in place of the default one. Spread the given `props` onto it. |
| yearFormat | "numeric" \| "2-digit" \| ((year: number) => string) \| undefined | - | No | No | How the year should be formatted in the select options. |
| years | number[] \| undefined | - | No | No | 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 | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Root

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[RangeCalendarRootSnippetProps]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| placeholder | DateValue \| undefined | - | No | Yes | The placeholder date, used to control the view of the calendar when no value is present. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| disabled | boolean \| undefined | - | No | No | Whether or not the calendar is disabled. |
| value | DateRange \| undefined | - | No | Yes | The value of the selected date range. |
| readonly | boolean \| undefined | - | No | No | 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> \| undefined | - | No | No | A callback function called when the value changes. |
| onPlaceholderChange | OnChangeFn<DateValue> \| undefined | - | No | No | A callback function called when the placeholder value changes. |
| preventDeselect | boolean \| undefined | - | No | No | Whether or not users can deselect a date once selected without selecting another date. |
| minValue | DateValue \| undefined | - | No | No | The minimum date that can be selected in the calendar. |
| maxValue | DateValue \| undefined | - | No | No | The maximum date that can be selected in the calendar. |
| pagedNavigation | boolean \| undefined | - | No | No | Applicable only when `numberOfMonths` is greater than 1. |
| weekStartsOn | 0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| undefined | - | No | No | 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" \| undefined | 'short' | No | No | How the string representation of the weekdays provided via the `weekdays` state store should be formatted. |
| isDateDisabled | DateMatcher \| undefined | - | No | No | A function that receives a date and returns `true` or `false` to indicate whether the date is disabled. |
| isDateUnavailable | DateMatcher \| undefined | - | No | No | 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 \| undefined | true | No | No | 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. |
| numberOfMonths | number \| undefined | - | No | No | Determines the number of months to display on the calendar simultaneously. For navigation between months, refer to the `pagedNavigation` prop. |
| calendarLabel | string \| undefined | - | No | No | 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 \| undefined | 'en-US' | No | No | The default locale setting. |
| disableDaysOutsideMonth | boolean \| undefined | true | No | No | 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. |
| maxDays | number \| undefined | - | No | No | The maximum number of days that can be selected in a range. |
| monthFormat | "numeric" \| "long" \| "short" \| "narrow" \| "2-digit" \| ((month: number) => string) \| undefined | - | No | No | The format of the month names in the calendar. |
| yearFormat | "numeric" \| "2-digit" \| ((year: number) => string) \| undefined | - | No | No | The format of the year names in the calendar. |
| onStartValueChange | OnChangeFn<DateValue \| undefined> \| undefined | - | No | No | 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> \| undefined | - | No | No | 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 \| undefined | - | No | No | The minimum number of days that can be selected in a range. |
| excludeDisabled | boolean \| undefined | - | No | No | 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 | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Cell

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[CalendarCellSnippetProps]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| date | CalendarDate \| CalendarDateTime \| ZonedDateTime | - | Yes | No | The date value of the cell. |
| month | CalendarDate \| CalendarDateTime \| ZonedDateTime | - | Yes | No | The month DateValue that this cell is being rendered in. |
| child | Snippet<[CalendarCellSnippetProps & { props: Record<string, unknown>; }]> \| undefined | - | No | No | Renders your own element in place of the default one. Spread the given `props` onto it. |
| element | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Day

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[CalendarDaySnippetProps]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the element. |
| style | string \| StyleProperties \| null \| undefined | - | No | No | Inline styles for the element. |
| child | Snippet<[CalendarDaySnippetProps & { props: Record<string, unknown>; }]> \| undefined | - | No | No | Renders your own element in place of the default one. Spread the given `props` onto it. |
| element | HTMLElement \| null \| undefined | null | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Month

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | string \| undefined | - | No | No | Classes added to the element. |
| month | { value: DateValue; weeks: DateValue[][]; dates: DateValue[]; } | - | Yes | No | Month data from the Root snippet. |
| weekdays | string[] | - | Yes | No | Weekday names from the Root snippet. |
| locale | string \| undefined | 'en-US' | No | No | BCP 47 locale used for names and number formats. |
| showHeading | boolean \| undefined | false | No | No | Shows the month name above the grid, for layouts with several months. |
| day | Snippet<[date: DateValue]> \| undefined | - | No | No | Custom content for each day cell. |



### Public types

~~~~ts
import type { DateValue } from '@internationalized/date';
import type { DefaultProps, WithElement } from '@mielui/svelte/utils';
import type { RangeCalendar as CalendarPrimitive, CalendarRootSnippetProps } from 'bits-ui';
import type { Snippet } from 'svelte';

export type RangeCalendarProps = WithElement<Omit<CalendarPrimitive.RootProps, 'child'>>;
export type RangeCalendarMonthProps = Omit<DefaultProps, 'children'> & {
    /** Month data from the Root snippet. */
    month: CalendarRootSnippetProps['months'][number];
    /** Weekday names from the Root snippet. */
    weekdays: string[];
    /** BCP 47 locale used for names and number formats. */
    locale?: string;
    /** Shows the month name above the grid, for layouts with several months. */
    showHeading?: boolean;
    /** Custom content for each day cell. */
    day?: Snippet<[date: DateValue]>;
};

export { default as Grid } from '../calendar/calendar-grid.svelte';
export { default as GridBody } from '../calendar/calendar-grid-body.svelte';
export { default as GridHead } from '../calendar/calendar-grid-head.svelte';
export { default as GridRow } from '../calendar/calendar-grid-row.svelte';
export { default as HeadCell } from '../calendar/calendar-head-cell.svelte';
export { default as Header } from '../calendar/calendar-header.svelte';
export { default as Heading } from '../calendar/calendar-heading.svelte';
export { default as MonthSelect } from '../calendar/calendar-month-select.svelte';
export { default as NextButton } from '../calendar/calendar-next-button.svelte';
export { default as PrevButton } from '../calendar/calendar-prev-button.svelte';
export { default as YearSelect } from '../calendar/calendar-year-select.svelte';
export { default as Root } from './range-calendar.svelte';
export { default as Cell } from './range-calendar-cell.svelte';
export { default as Day } from './range-calendar-day.svelte';
export { default as Month } from './range-calendar-month.svelte';
~~~~

## Examples

### Constraints

~~~~svelte
<script lang="ts">
    import { CalendarDate, type DateValue } from '@internationalized/date';
    import * as RangeCalendar from '@mielui/svelte/components/range-calendar';

    const start = new CalendarDate(2026, 9, 1);
    const end = new CalendarDate(2026, 9, 30);
    let value = $state<{
        start: DateValue | undefined;
        end: DateValue | undefined;
    }>({
        start: undefined,
        end: undefined
    });
</script>

<RangeCalendar.Root
    bind:value
    placeholder={start}
    minValue={start}
    maxValue={end}
    minDays={2}
    maxDays={7}
    calendarLabel="September booking, two to seven days"
>
    {#snippet children({ months, weekdays })}
        <RangeCalendar.Header>
            <RangeCalendar.Heading />
        </RangeCalendar.Header>
        {#each months as month (month.value.toString())}
            <RangeCalendar.Month {month} {weekdays} />
        {/each}
    {/snippet}
</RangeCalendar.Root>
~~~~

### Events

~~~~svelte
<script lang="ts">
    import { CalendarDate } from '@internationalized/date';
    import * as RangeCalendar from '@mielui/svelte/components/range-calendar';

    let start = $state('not picked');
    let end = $state('not picked');
</script>

<div class="flex flex-col items-center gap-3">
    <RangeCalendar.Root
        calendarLabel="Stay"
        placeholder={new CalendarDate(2026, 9, 1)}
        onStartValueChange={(date) => {
            start = date?.toString() ?? 'not picked';
        }}
        onEndValueChange={(date) => {
            end = date?.toString() ?? 'not picked';
        }}
    />
    <p class="text-sm text-foreground-muted">Check-in {start}. Check-out {end}.</p>
</div>
~~~~

### Exclude Disabled

~~~~svelte
<script lang="ts">
    import { CalendarDate, isWeekend } from '@internationalized/date';
    import * as RangeCalendar from '@mielui/svelte/components/range-calendar';
</script>

<RangeCalendar.Root
    calendarLabel="Workdays only"
    placeholder={new CalendarDate(2026, 9, 1)}
    isDateDisabled={(date) => isWeekend(date, 'en-US')}
    excludeDisabled
/>
~~~~

### Hero

~~~~svelte
<script lang="ts">
    import { CalendarDate, type DateValue } from '@internationalized/date';
    import * as RangeCalendar from '@mielui/svelte/components/range-calendar';

    let value = $state<{
        start: DateValue | undefined;
        end: DateValue | undefined;
    }>({
        start: new CalendarDate(2026, 9, 17),
        end: new CalendarDate(2026, 9, 23)
    });
</script>

<div class="flex max-w-full flex-col items-center gap-4">
    <div class="mielui-plate max-w-full">
        <RangeCalendar.Root bind:value calendarLabel="Travel dates" />
    </div>
    <p class="text-center text-sm text-foreground-muted">
        {value.start?.toString() ?? 'Start date'}
        <span aria-hidden="true">→</span>
        {value.end?.toString() ?? 'End date'}
    </p>
</div>
~~~~

### Two Months

~~~~svelte
<script lang="ts">
    import { CalendarDate, 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>

<div class="flex max-w-full flex-col gap-3">
    <RangeCalendar.Root
        bind:value
        calendarLabel="Booking dates"
        placeholder={new CalendarDate(2026, 9, 17)}
        numberOfMonths={2}
        pagedNavigation
    />
    <p aria-live="polite" class="px-3 text-sm text-foreground-muted">
        {#if value.start && value.end}
            {`${value.start.toString()} to ${value.end.toString()}`}
        {:else if value.start}
            {`Start: ${value.start.toString()}. Choose an end date.`}
        {:else}
            Choose a start and end date, across any month.
        {/if}
    </p>
</div>
~~~~

For the rendered reference, visit [/docs/components/range-calendar](/docs/components/range-calendar).
