DateRangePicker
Pick a start and end date through a single field.
The <DateRangePicker> component lets users enter or select a date range, a start date together with an end date, through a single field.
The two dates can be picked together on a calendar or typed directly into the field.
Anatomy
The Label states the purpose of the field. Two date inputs hold the start and the end of the range, with a separator between them. The Calendar Button opens a range calendar where the user can pick both ends visually.
Appearance
The appearance of a component can be customized using the variant and size props. These props adjust the visual style and dimensions of the component, available values are based on the active theme.
| Property | Type | Description |
|---|---|---|
variant | - | The available variants of this component. |
size | - | The available sizes of this component. |
Usage
The clearest signal that you want a <DateRangePicker> is the span itself. When the time between the two dates is part of what the user is deciding, a trip that lasts a week, a report that covers a quarter, a campaign that runs for ten days, the two dates work as one value and people benefit from seeing the whole stretch on a single calendar.
Choosing both ends on the same calendar also keeps that value consistent. The end can never fall before the start, and rules like a minimum number of nights or blocked dates apply to the pair at once. Two separate fields cannot do this without extra validation, and moving between two calendars makes it easy to click the wrong day once the visible month shifts.
Do
Use a <DateRangePicker> when the time between the two dates matters and
people benefit from seeing the whole span on one calendar, like a booking
or a reporting period.
Don't
Don't use it when only the endpoints matter, when one end can stay open, or when the dates are unrelated. Reach for a single DatePicker or two independent fields instead.
The same booking captures one value on the left and two on the right. The range picker holds the whole trip in a single control and lets people pick both ends on one calendar, while the separate fields treat arrival and departure as unrelated inputs.
import { DatePicker, DateRangePicker, Inline, Stack, Text,} from '@marigold/components';export default () => ( <Inline space={12} alignY="top"> <Stack space={3}> <Text weight="semibold">One range picker</Text> <DateRangePicker label="Trip dates" /> {/* [!code highlight] */} </Stack> <Stack space={3}> <Text weight="semibold">Two separate fields</Text> <Inline space={4}> <DatePicker label="Arrival" width="fit" /> <DatePicker label="Departure" width="fit" /> </Inline> </Stack> </Inline>);@internationalized/date
Marigold represents dates with the DateValue type from
@internationalized/date rather than the native JavaScript Date, which
avoids the timezone and parsing problems that come with Date. It is a peer
dependency, so install it if it is not already in your project.
Limiting the range
Often only part of the calendar makes sense. A booking cannot start in the past, and a report cannot reach into the future. Set minValue and maxValue to fence the selectable window, and the calendar prevents anything outside it.
import { getLocalTimeZone, today } from '@internationalized/date';import { DateRangePicker } from '@marigold/components';export default () => { const now = today(getLocalTimeZone()); return ( <DateRangePicker label="Booking window" minValue={now} // [!code highlight] maxValue={now.add({ months: 6 })} // [!code highlight] /> );};Range length
A range often has rules about how long it can be, such as a stay of at least two nights or at most two weeks. Read the selected value, compare its end to its start, and set error with an errorMessage when the length falls outside the allowed bounds.
import type { DateValue } from '@internationalized/date';import { useState } from 'react';import { DateRangePicker } from '@marigold/components';const MIN_NIGHTS = 2;const MAX_NIGHTS = 14;const getErrorMessage = (nights: number | null): string | undefined => { if (nights === null) return undefined; if (nights < MIN_NIGHTS) return `A stay must be at least ${MIN_NIGHTS} nights.`; if (nights > MAX_NIGHTS) return `A stay can be at most ${MAX_NIGHTS} nights.`; return undefined;};export default () => { const [value, setValue] = useState<{ start: DateValue; end: DateValue; } | null>(null); const nights = value ? value.end.compare(value.start) : null; // [!code highlight] const message = getErrorMessage(nights); return ( <DateRangePicker label="Stay" value={value} onChange={setValue} error={Boolean(message)} // [!code highlight] errorMessage={message} // [!code highlight] /> );};Blocking unavailable dates
Some days inside the allowed window still cannot be chosen, such as nights that are already booked or public holidays. Return true from dateUnavailable for any date that should be disabled.
import { getLocalTimeZone, today } from '@internationalized/date';import { DateRangePicker } from '@marigold/components';export default () => { const now = today(getLocalTimeZone()); // Nights that are already booked and cannot be part of a new stay. const booked = [ now.add({ days: 3 }), now.add({ days: 4 }), now.add({ days: 5 }), ]; return ( <DateRangePicker label="Stay dates" minValue={now} dateUnavailable={date => booked.some(d => date.compare(d) === 0)} // [!code highlight] /> );};Showing multiple months
Ranges that stretch across several months are awkward to pick one month at a time, which is common when planning a holiday or a quarter. Set visibleDuration to show up to three months at once. On small screens the calendar always falls back to a single month inside a tray.
import { DateRangePicker } from '@marigold/components';export default () => ( <DateRangePicker label="Vacation" visibleDuration={{ months: 2 }} // [!code highlight] />);Narrow screens
The field lays out two date inputs, a separator and the calendar button next to each other, so it needs roughly 260px of width for its content. It holds that minimum even when the surrounding layout is narrower and overflows rather than crushing the inputs together. Because of this it is not suited to very small viewports such as a 320px screen. Give it horizontal room, or reach for a single DatePicker when space is tight.
Accessibility
Picking a range on the calendar can be slow for people who rely on a keyboard or a screen reader, and people who work with dates all day often prefer to type. For both groups the field stays fully usable without ever opening the calendar. Every segment can be edited directly, and a full date can be pasted into either the start or the end input. The component parses the pasted text, validates it, and fills in that end of the range. Pasted text is recognized in these formats:
- ISO format:
2023-12-25 - European format:
25.12.2023or25/12/2023 - US format:
12/25/2023or12-25-2023
Unrecognized or invalid text is ignored, so the field never ends up in a broken state.
Props
Prop
Type
Accessibility props (4)
Prop
Type
DOM event handlers (64)
Prop
Type
Alternative components
DatePicker: Pick a single date through a field with a calendar popover.
RangeCalendar: Select a date range from a calendar that is always visible.
DateField: Let users type a single date directly.