RangeCalendar

RangeCalendar provides a customizable calendar interface for date range selection.

The component is built with accessibility in mind and follows ARIA best practices for calendar widgets. It provides keyboard navigation, screen reader support, and customizable labels for internationalization.

import { RangeCalendar } from '@wordpress/ui';

View on Storybook

View source on GitHub

PropsPermalink to this section

NameDefaultDescription
role'application'

AriaRole | undefined

The ARIA role for the calendar’s root element.

The default application role helps assistive technologies pass calendar navigation keys to the component. Its accessible name includes the current month.

Changing this role can affect screen-reader keyboard navigation. Only override it for a tested composition. Apply roles that require additional behavior, such as dialog, to a wrapper.

requiredfalse

boolean

Whether the selection is required. When true, there always needs to be a date selected.

defaultMonthThe current month

Date

The initial month to show in the calendar view (uncontrolled).

month

Date

The month displayed in the calendar view (controlled). Use together with onMonthChange to change the month programmatically.

numberOfMonths1

number

The number of months displayed at once.

showOutsideDaysfalse

boolean

When true, days from adjacent months are shown in the grid and receive the outside modifier and the class.

fixedWeeksfalse

boolean

When true, the calendar always shows a fixed number of weeks (e.g. 6) so the grid height does not change between months.

startMonth

Date

The earliest month to start the month navigation.

endMonth

Date

The latest month to end the month navigation.

autoFocus

boolean

Focus the first selected day (if set) or today’s date (if not disabled).

Use this prop when the calendar should receive initial focus as it opens, such as in a calendar popover. Do not use it to move focus after updates to an open calendar.

disabled

Matcher | Matcher[] | undefined

Specify which days are disabled. Using true will disable all dates.

disableNavigation

boolean

Disable the navigation buttons.

labels

{ labelNav?: (() => string) | undefined; labelGrid?: ((date: Date) => string) | undefined; labelGridcell?: ((date: Date, modifiers?: Modifiers | undefined) => string) | undefined; labelNext?: ((month: Date | undefined) => string) | undefined; labelPrevious?: ((month: Date | undefined) => string) | undefined; labelDa...

Use custom labels, useful for translating the component.

For a correct localized experience, consumers should make sure the locale used for translated labels and date text is consistent.

localeThe `enUS` locale from `date-fns/locale`

string | Locale | undefined

A BCP 47 locale code or date-fns locale object used to localize date text, numerals, the default text direction, and the first day of the week.

The locale code derives the first day of the week when the browser provides that information, whether passed directly or through a date-fns locale object. Use weekStartsOn to override this default.

Invalid or unsupported locale codes fall back to en-US for date text. A date-fns locale object with an unsupported code retains its own first-day setting.

For a correct localized experience, consumers should make sure the locale used for translated labels and date text is consistent.

The calendar always uses a Gregorian date grid. The locale does not change the underlying calendar system.

weekStartsOnBased on the `locale` prop when available

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

The index of the first day of the week (0 – Sunday). Overrides the locale’s one.

onMonthChange

(month: Date) => void

Event fired when the user navigates between months.

timeZone

string

The time zone (IANA or UTC offset) to use in the calendar.

See Wikipedia for the possible values.

When working with time zones, use the TZDate object from the @date-fns/tz package instead of the native Date object.

render

ComponentRenderFn<HTMLAttributesWithRef<any>> | ReactElement<Record<string, unknown>, string | JSXElementConstructor<any>> | undefined

Replaces the component’s default HTML element using a given React element, or a function that returns a React element.

style

CSSProperties

CSS style to apply to the element.

className

string

CSS class name to apply to the element.

excludeDisabled

boolean

When true, the range will reset when including a disabled day.

resetOnSelecttrue

boolean

When true, clicking a day starts a new range if there is no current start date or if a range is already complete. In those cases, the clicked day becomes the start of the new range. When required is false, clicking the same day of a single-day range clears the selection.

min

number

The minimum number of nights to include in the range.

max

number

The maximum number of nights to include in the range.

value

DateRange | null | undefined

The selected range (controlled). Use null when there is no selection. To render an uncontrolled calendar, use defaultValue instead.

onValueChange

OnValueChangeHandler<DateRange | null>

Event handler called when the selected range changes.

defaultValue

DateRange

The initially selected range (uncontrolled).

ExamplesPermalink to this section

DefaultPermalink to this section

const Default = () => <RangeCalendar onMonthChange={fn()} onValueChange={fn()} endMonth={ toDate( endMonth ) } />;

Disabled DatesPermalink to this section

const DisabledDates = () => <RangeCalendar
    onMonthChange={fn()}
    onValueChange={fn()}
    disabled={DISABLED_DATES_SAMPLE}
    endMonth={ toDate( endMonth ) } />;

With Selected Range And MonthPermalink to this section

const WithSelectedRangeAndMonth = () => <RangeCalendar
    onMonthChange={fn()}
    onValueChange={fn()}
    defaultValue={{
        from: firstDayOfNextMonth,
        to: fourthDayOfNextMonth,
    }}
    defaultMonth={firstDayOfNextMonth}
    endMonth={ toDate( endMonth ) } />;

With Range ConstraintsPermalink to this section

Use min and max to constrain the number of nights the range may span, and excludeDisabled to reset the range when it would include a disabled day.

const WithRangeConstraints = () => <RangeCalendar
    onMonthChange={fn()}
    onValueChange={fn()}
    min={2}
    max={7}
    excludeDisabled
    disabled={{ dayOfWeek: [ 0, 6 ] }}
    endMonth={ toDate( endMonth ) } />;

With Outside DaysPermalink to this section

Shows days from adjacent months in the grid. Outside days use a lighter style and are still interactive. Use fixedWeeks to keep the grid height constant.

const WithOutsideDays = () => <RangeCalendar
    onMonthChange={fn()}
    onValueChange={fn()}
    showOutsideDays
    fixedWeeks
    endMonth={ toDate( endMonth ) } />;

With Time ZonePermalink to this section

When working with time zones, use the TZDate object from the @date-fns/tz package instead of the native Date object.

const WithTimeZone = () => {
    const [ range, setRange ] = useState< typeof args.value >( null );

    useEffect( () => {
        setRange(
            // Select from one week from today to two weeks from today
            // every time the timezone changes.
            {
                from: new TZDate(
                    new Date().setDate( new Date().getDate() + 7 ),
                    args.timeZone
                ),
                to: new TZDate(
                    new Date().setDate( new Date().getDate() + 14 ),
                    args.timeZone
                ),
            }
        );
    }, [ args.timeZone ] );

    return (
        <>
            <RangeCalendar
                onMonthChange={fn()}
                timeZone="Pacific/Auckland"
                endMonth={ toDate( endMonth ) }
                value={ range }
                onValueChange={ ( selectedDate, ...rest ) => {
                    setRange(
                        // Set controlled state to null if there's no selection
                        ! selectedDate ||
                            ( selectedDate.from === undefined &&
                                selectedDate.to === undefined )
                            ? null
                            : selectedDate
                    );
                    args.onValueChange?.( selectedDate, ...rest );
                } }
                disabled={ [
                    {
                        // Disable any date before today
                        before: new TZDate( new Date(), args.timeZone ),
                    },
                ] } />
            <p>Calendar set to { args.timeZone ?? 'current' }timezone,
                                    disabling selection for all dates before today, and starting
                                    with a default date range of 1 week from today to 2 weeks
                                    from today.
                                </p>
        </>
    );
};