Date range & multiple dates
Pick a check-in / check-out date range, or several separate dates, in React Native. DatePicker (and Calendar) switch selection model with mode. TypeScript narrows value and onChange for each mode.
mode | value | onChange | Extra props |
|---|---|---|---|
'single' (default) | Date | null | (date: Date) => void | โ |
'range' | DateRange ({ start: Date | null; end: Date | null }) | (range: DateRange) => void | minRangeDays, maxRangeDays, allowSingleDayRange |
'multiple' | Date[] | (dates: Date[]) => void | maxSelections |
Try the propsโ
modemaxRangeDaysmaxSelections<DatePicker
value={value}
onChange={setValue}
mode="range"
maxRangeDays={14}
maxSelections={5}
/>
Rangeโ
import { useState } from 'react'; import { Text } from 'react-native'; import { DatePicker, formatDate } from '@elik56a/react-native-pickers'; import type { DateRange } from '@elik56a/react-native-pickers'; export default function Example() { const [range, setRange] = useState<DateRange>({ start: null, end: null }); const nights = range.start && range.end ? Math.round((+range.end - +range.start) / 864e5) : 0; return ( <> <DatePicker mode="range" value={range} onChange={setRange} minDate={new Date()} /> <Text style={{ textAlign: 'center', color: '#6E685E' }}> {range.start ? formatDate(range.start) : 'Check-in'} โ {range.end ? formatDate(range.end) : 'Check-out'} {nights ? ` ยท ${nights} night${nights > 1 ? 's' : ''}` : ''} </Text> </> ); }
How taps workโ
- First tap sets the start:
onChange({ start: day, end: null }). - Second tap on or after the start sets the end:
onChange({ start, end: day }). - A tap before the start, or any tap after a complete range, starts a new range from that day.
The band between start and end uses primaryMuted; the ends use primary.
Ranges never cross disabled daysโ
If the days between start and the tapped day include a disabled one (from minDate/maxDate, isDateDisabled or markedDates), the tap starts a new range instead of producing a range that spans an unavailable night. That's exactly what booking flows need:
import { useState } from 'react'; import { DatePicker, toDateKey, addDays } from '@elik56a/react-native-pickers'; import type { DateRange } from '@elik56a/react-native-pickers'; export default function Example() { const today = new Date(); const booked = new Set([toDateKey(addDays(today, 4)), toDateKey(addDays(today, 5)), toDateKey(addDays(today, 12))]); const [range, setRange] = useState<DateRange>({ start: null, end: null }); return ( <DatePicker mode="range" value={range} onChange={setRange} minDate={today} isDateDisabled={(d) => booked.has(toDateKey(d))} /> ); }
Minimum and maximum lengthโ
Length is measured in days between start and end (nights): a range from the 10th to the 13th is 3.
| Prop | Effect when violated |
|---|---|
minRangeDays | A second tap that would be too short is ignored; the start stays selected |
maxRangeDays | A second tap that would be too long starts a new range from that day |
allowSingleDayRange={false} | Tapping the start again is ignored (by default it makes a one-day range, start === end) |
import { useState } from 'react'; import { Text } from 'react-native'; import { DatePicker } from '@elik56a/react-native-pickers'; import type { DateRange } from '@elik56a/react-native-pickers'; export default function Example() { const [range, setRange] = useState<DateRange>({ start: null, end: null }); return ( <> <Text style={{ textAlign: 'center', marginBottom: 4, color: '#6E685E' }}>Stay 2 โ 7 nights</Text> <DatePicker mode="range" value={range} onChange={setRange} minDate={new Date()} minRangeDays={2} maxRangeDays={7} allowSingleDayRange={false} /> </> ); }
Range stylingโ
import { useState } from 'react'; import { DatePicker, addDays } from '@elik56a/react-native-pickers'; import type { DateRange } from '@elik56a/react-native-pickers'; export default function Example() { const t = new Date(); const [range, setRange] = useState<DateRange>({ start: addDays(t, 2), end: addDays(t, 6) }); return ( <DatePicker mode="range" value={range} onChange={setRange} theme={{ colors: { primary: '#E11D48', primaryMuted: '#FFE4E6' } }} styles={{ selectedDay: { borderRadius: 10 }, rangeBand: { borderRadius: 0 } }} /> ); }
Multipleโ
Tap days to toggle them. onChange always receives the dates sorted ascending. With maxSelections, further taps on unselected days are ignored once the limit is reached (deselecting still works).
import { useState } from 'react'; import { Text } from 'react-native'; import { DatePicker, formatDate } from '@elik56a/react-native-pickers'; export default function Example() { const [dates, setDates] = useState<Date[]>([]); return ( <> <DatePicker mode="multiple" value={dates} onChange={setDates} maxSelections={5} minDate={new Date()} /> <Text style={{ textAlign: 'center', color: '#6E685E' }}> {dates.length ? dates.map((d) => formatDate(d, undefined, { day: 'numeric', month: 'short' })).join(', ') : 'Pick up to 5 days'} </Text> </> ); }
Uncontrolledโ
<DatePicker mode="range" defaultValue={{ start: new Date(), end: null }} onChange={(r) => console.log(r)} />
<DatePicker mode="multiple" defaultValue={[]} onChange={(d) => console.log(d)} />
In a sheetโ
DatePickerSheet disables Done until the range is complete (both start and end).
<DatePickerSheet
visible={open}
onClose={() => setOpen(false)}
title="Your stay"
mode="range"
value={range}
onConfirm={setRange}
minDate={new Date()}
minRangeDays={1}
/>
โ DatePickerSheet
Propsโ
All calendar props apply. Mode-specific props:
| Prop | Mode | Type | Default | Description |
|---|---|---|---|---|
minRangeDays | range | number | โ | Minimum nights (end โ start in days). Shorter second taps are ignored. |
maxRangeDays | range | number | โ | Maximum nights. Longer second taps start a new range. |
allowSingleDayRange | range | boolean | true | Allow start === end. |
maxSelections | multiple | number | โ | Maximum number of selected days. |
Working with rangesโ
The date utilities make common range checks one-liners:
import { isBetweenDays, isSameDay, compareDay, addDays } from '@elik56a/react-native-pickers';
const nights = Math.round((range.end!.getTime() - range.start!.getTime()) / 86_400_000);
const includesToday = isSameDay(range.start, new Date()) || isBetweenDays(new Date(), range.start, range.end);
const isFuture = compareDay(range.start!, new Date()) > 0;