Date Range Picker
A component that allows users to select a date range from a calendar.
A component that allows users to select a date range from a calendar.
M | T | W | T | F | S | S |
---|---|---|---|---|---|---|
To set up the date picker correctly, you’ll need to understand its anatomy and how we name its parts.
Each part includes a
data-part
attribute to help identify them in the DOM.
To create a Date Picker that allows a range selection, set the selectionMode
prop to “range”. This renders a single grid view where the user can select a
start and end date:
import { DatePicker, Portal } from '@ark-ui/react'
const Basic = () => {
return (
<DatePicker.Root selectionMode="range">
<DatePicker.Label>Label</DatePicker.Label>
<DatePicker.Control>
<DatePicker.Input />
<DatePicker.Trigger>📅</DatePicker.Trigger>
<DatePicker.ClearTrigger>Clear</DatePicker.ClearTrigger>
</DatePicker.Control>
<Portal>
<DatePicker.Positioner>
<DatePicker.Content>
<DatePicker.YearSelect />
<DatePicker.MonthSelect />
<DatePicker.View view="day">
{(api) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger>Prev</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger>Next</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableHead>
<DatePicker.TableRow>
{api.weekDays.map((weekDay, id) => (
<DatePicker.TableHeader key={id}>{weekDay.short}</DatePicker.TableHeader>
))}
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody>
{api.weeks.map((week, id) => (
<DatePicker.TableRow key={id}>
{week.map((day, id) => (
<DatePicker.TableCell key={id} value={day}>
<DatePicker.TableCellTrigger>{day.day}</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.View>
<DatePicker.View view="month">
{(api) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger>Prev</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger>Next</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableBody>
{api.getMonthsGrid({ columns: 4, format: 'short' }).map((months, id) => (
<DatePicker.TableRow key={id}>
{months.map((month, id) => (
<DatePicker.TableCell key={id} value={month.value}>
<DatePicker.TableCellTrigger>
{month.label}
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.View>
<DatePicker.View view="year">
{(api) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger>Prev</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger>Next</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableBody>
{api.getYearsGrid({ columns: 4 }).map((years, id) => (
<DatePicker.TableRow key={id}>
{years.map((year, id) => (
<DatePicker.TableCell key={id} value={year.value}>
<DatePicker.TableCellTrigger>
{year.label}
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.View>
</DatePicker.Content>
</DatePicker.Positioner>
</Portal>
</DatePicker.Root>
)
}
import { DatePicker } from '@ark-ui/solid'
import { Portal } from 'solid-js/web'
const Basic = () => {
return (
<DatePicker.Root selectionMode="range">
<DatePicker.Label>Label</DatePicker.Label>
<DatePicker.Control>
<DatePicker.Input />
<DatePicker.Trigger>📅</DatePicker.Trigger>
<DatePicker.ClearTrigger>Clear</DatePicker.ClearTrigger>
</DatePicker.Control>
<Portal>
<DatePicker.Positioner>
<DatePicker.Content>
<DatePicker.YearSelect />
<DatePicker.MonthSelect />
<DatePicker.View view="day">
{(api) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger>Prev</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger>Next</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableHead>
<DatePicker.TableRow>
{api().weekDays.map((weekDay) => (
<DatePicker.TableHeader>{weekDay.short}</DatePicker.TableHeader>
))}
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody>
{api().weeks.map((week) => (
<DatePicker.TableRow>
{week.map((day) => (
<DatePicker.TableCell value={day}>
<DatePicker.TableCellTrigger>{day.day}</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.View>
<DatePicker.View view="month">
{(api) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger>Prev</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger>Next</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableBody>
{api()
.getMonthsGrid({ columns: 4, format: 'short' })
.map((months) => (
<DatePicker.TableRow>
{months.map((month) => (
<DatePicker.TableCell value={month.value}>
<DatePicker.TableCellTrigger>
{month.label}
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.View>
<DatePicker.View view="year">
{(api) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger>Prev</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger>Next</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableBody>
{api()
.getYearsGrid({ columns: 4 })
.map((years) => (
<DatePicker.TableRow>
{years.map((year) => (
<DatePicker.TableCell value={year.value}>
<DatePicker.TableCellTrigger>
{year.label}
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.View>
</DatePicker.Content>
</DatePicker.Positioner>
</Portal>
</DatePicker.Root>
)
}
<script setup lang="ts">
import { DatePicker } from '@ark-ui/vue'
</script>
<template>
<DatePicker.Root selection-mode="range">
<DatePicker.Label>Label</DatePicker.Label>
<DatePicker.Control>
<DatePicker.Input />
<DatePicker.Trigger>📅</DatePicker.Trigger>
<DatePicker.ClearTrigger>Clear</DatePicker.ClearTrigger>
</DatePicker.Control>
<Teleport to="body">
<DatePicker.Positioner>
<DatePicker.Content>
<DatePicker.YearSelect />
<DatePicker.MonthSelect />
<DatePicker.View view="day" #default="api">
<DatePicker.ViewControl>
<DatePicker.PrevTrigger>Prev</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger>Next</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableHead>
<DatePicker.TableRow>
<DatePicker.TableHeader v-for="(weekDay, id) in api.weekDays" :key="id">{{
weekDay.short
}}</DatePicker.TableHeader>
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody>
<DatePicker.TableRow v-for="(week, id) in api.weeks" :key="id">
<DatePicker.TableCell v-for="(day, id) in week" :key="id" :value="day">
<DatePicker.TableCellTrigger>{{ day.day }}</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
</DatePicker.TableRow>
</DatePicker.TableBody>
</DatePicker.Table>
</DatePicker.View>
<DatePicker.View view="month" #default="api">
<DatePicker.ViewControl>
<DatePicker.PrevTrigger>Prev</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger>Next</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableBody>
<DatePicker.TableRow
v-for="(months, id) in api.getMonthsGrid({ columns: 4, format: 'short' })"
:key="id"
>
<DatePicker.TableCell
v-for="(month, id) in months"
:key="id"
:value="month.value"
>
<DatePicker.TableCellTrigger>{{ month.label }}</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
</DatePicker.TableRow>
</DatePicker.TableBody>
</DatePicker.Table>
</DatePicker.View>
<DatePicker.View view="year" #default="api">
<DatePicker.ViewControl>
<DatePicker.PrevTrigger>Prev</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger>Next</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableBody>
<DatePicker.TableRow
v-for="(years, id) in api.getYearsGrid({ columns: 4 })"
:key="id"
>
<DatePicker.TableCell v-for="(year, id) in years" :key="id" :value="year.value">
<DatePicker.TableCellTrigger>{{ year.label }}</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
</DatePicker.TableRow>
</DatePicker.TableBody>
</DatePicker.Table>
</DatePicker.View>
</DatePicker.Content>
</DatePicker.Positioner>
</Teleport>
</DatePicker.Root>
</template>
In some cases, you might want to display a non-dismissable date picker. This can
be achieved by setting the open
prop to true
and closeOnSelect
prop to
false
.
Additionaly, you may want to display two consecutive months at the same time to
facilitate a broader range selection. You can do it by setting the numOfMonths
prop to 2
:
import { DatePicker } from '@ark-ui/react'
const Standalone = () => {
return (
<DatePicker.Root open={true} numOfMonths={2} selectionMode="range" closeOnSelect={false}>
{(api) => {
const offset = api.getOffset({ months: 1 })
return (
<DatePicker.View view="day">
<DatePicker.ViewControl>
<DatePicker.PrevTrigger>Prev</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger>Next</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<div style={{ display: 'flex', gap: '24px' }}>
<DatePicker.Table>
<DatePicker.TableHead>
<DatePicker.TableRow>
{api.weekDays.map((weekDay, id) => (
<DatePicker.TableHeader key={id}>{weekDay.short}</DatePicker.TableHeader>
))}
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody>
{api.weeks.map((week, id) => (
<DatePicker.TableRow key={id}>
{week.map((day, id) => (
<DatePicker.TableCell key={id} value={day}>
<DatePicker.TableCellTrigger>{day.day}</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
{/* 2nd month */}
<DatePicker.Table>
<DatePicker.TableHead>
<DatePicker.TableRow>
{api.weekDays.map((weekDay, id) => (
<DatePicker.TableHeader key={id}>{weekDay.short}</DatePicker.TableHeader>
))}
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody>
{offset.weeks.map((week, id) => (
<DatePicker.TableRow key={id}>
{week.map((day, id) => (
<DatePicker.TableCell
key={id}
value={day}
visibleRange={offset.visibleRange}
>
<DatePicker.TableCellTrigger>{day.day}</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</div>
</DatePicker.View>
)
}}
</DatePicker.Root>
)
}
import { DatePicker } from '@ark-ui/solid'
const Standalone = () => {
return (
<DatePicker.Root open={true} numOfMonths={2} selectionMode="range" closeOnSelect={false}>
{(api) => {
const offset = api().getOffset({ months: 1 })
return (
<DatePicker.View view="day">
<DatePicker.ViewControl>
<DatePicker.PrevTrigger>Prev</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger>Next</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<div style={{ display: 'flex', gap: '24px' }}>
<DatePicker.Table>
<DatePicker.TableHead>
<DatePicker.TableRow>
{api().weekDays.map((weekDay) => (
<DatePicker.TableHeader>{weekDay.short}</DatePicker.TableHeader>
))}
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody>
{api().weeks.map((week) => (
<DatePicker.TableRow>
{week.map((day) => (
<DatePicker.TableCell value={day}>
<DatePicker.TableCellTrigger>{day.day}</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
{/* 2nd month */}
<DatePicker.Table>
<DatePicker.TableHead>
<DatePicker.TableRow>
{api().weekDays.map((weekDay) => (
<DatePicker.TableHeader>{weekDay.short}</DatePicker.TableHeader>
))}
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody>
{offset.weeks.map((week) => (
<DatePicker.TableRow>
{week.map((day) => (
<DatePicker.TableCell value={day} visibleRange={offset.visibleRange}>
<DatePicker.TableCellTrigger>{day.day}</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</div>
</DatePicker.View>
)
}}
</DatePicker.Root>
)
}
<script setup lang="ts">
import { DatePicker } from '@ark-ui/vue'
</script>
<template>
<DatePicker.Root
open
:num-of-months="2"
selection-mode="range"
:close-on-select="false"
#default="api"
>
<DatePicker.View view="day">
<DatePicker.ViewControl>
<DatePicker.PrevTrigger>Prev</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger>Next</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<div style="display: flex; gap: 24px">
<DatePicker.Table>
<DatePicker.TableHead>
<DatePicker.TableRow>
<DatePicker.TableHeader v-for="(weekDay, id) in api.weekDays" :key="id">{{
weekDay.short
}}</DatePicker.TableHeader>
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody>
<DatePicker.TableRow v-for="(week, id) in api.weeks" :key="id">
<DatePicker.TableCell v-for="(day, id) in week" :key="id" :value="day">
<DatePicker.TableCellTrigger>{{ day.day }}</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
</DatePicker.TableRow>
</DatePicker.TableBody>
</DatePicker.Table>
<!-- 2nd month -->
<DatePicker.Table>
<DatePicker.TableHead>
<DatePicker.TableRow>
<DatePicker.TableHeader v-for="(weekDay, id) in api.weekDays" :key="id">{{
weekDay.short
}}</DatePicker.TableHeader>
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody>
<DatePicker.TableRow v-for="(week, id) in offset" :key="id">
<!-- TODO: make working standalone -->
<!-- <DatePicker.TableCell
v-for="(day, id) in week"
:key="id"
:value="day"
:visible-range="offset.visibleRange"
>
<DatePicker.TableCellTrigger>{{ day.day }}</DatePicker.TableCellTrigger>
</DatePicker.TableCell> -->
</DatePicker.TableRow>
</DatePicker.TableBody>
</DatePicker.Table>
</div>
</DatePicker.View>
</DatePicker.Root>
</template>
asChild Render as a different element type. | boolean | |
closeOnSelect Whether the calendar should close after the date selection is complete. This is ignored when the selection mode is `multiple`. | boolean | true |
defaultValue The initial value of the date picker | string[] | |
dir The document's text/writing direction. | 'ltr' | 'rtl' | "ltr" |
disabled Whether the calendar is disabled. | boolean | |
fixedWeeks Whether the calendar should have a fixed number of weeks. This renders the calendar with 6 weeks instead of 5 or 6. | boolean | |
focusedValue The focused date. | string | |
format The format of the date to display in the input. | (date: DateValue[]) => string | |
getRootNode A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron. | () => Node | ShadowRoot | Document | |
id The unique identifier of the machine. | string | |
ids The ids of the elements in the date picker. Useful for composition. | Partial<{ root: string; table(id: string): string; tableHeader(id: string): string; tableBody(id: string): string; tableRow(id: string): string; content: string; cellTrigger(id: string): string; ... 9 more ...; positioner: string; }> | |
isDateUnavailable Returns whether a date of the calendar is available. | (date: DateValue, locale: string) => boolean | |
lazyMount Whether to enable lazy mounting | boolean | false |
locale The locale (BCP 47 language tag) to use when formatting the date. | string | |
max The maximum date that can be selected. | DateValue | |
min The minimum date that can be selected. | DateValue | |
modal Whether the calendar should be modal. This means that the calendar will block interaction with the rest of the page, and trap focus within it. | boolean | |
name The `name` attribute of the input element. | string | |
numOfMonths The number of months to display. | number | |
onExitComplete Function called when the animation ends in the closed state. | () => void | |
onFocusChange Function called when the focused date changes. | (details: FocusChangeDetails) => void | |
onOpenChange Function called when the calendar opens or closes. | (details: OpenChangeDetails) => void | |
onValueChange Function called when the value changes. | (details: ValueChangeDetails) => void | |
onViewChange Function called when the view changes. | (details: ViewChangeDetails) => void | |
open Whether the datepicker is open | boolean | |
parse The format of the date to display in the input. | (value: string) => DateValue[] | |
positioning The user provided options used to position the date picker content | PositioningOptions | |
present Whether the node is present (controlled by the user) | boolean | |
readOnly Whether the calendar is read-only. | boolean | |
selectionMode The selection mode of the calendar. - `single` - only one date can be selected - `multiple` - multiple dates can be selected - `range` - a range of dates can be selected | SelectionMode | |
startOfWeek The first day of the week. `0` - Sunday `1` - Monday `2` - Tuesday `3` - Wednesday `4` - Thursday `5` - Friday `6` - Saturday | number | |
timeZone The time zone to use | string | |
translations The localized messages to use. | IntlTranslations | |
unmountOnExit Whether to unmount on exit. | boolean | false |
value The value of the date picker | string[] | |
view The view of the calendar | DateView | "day" |
view | DateView | |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean | |
columns | number |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
value | number | DateValue | |
asChild Render as a different element type. | boolean | |
columns | number | |
disabled | boolean | |
visibleRange | VisibleRange |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
asChild Render as a different element type. | boolean |
Previous
Date PickerNext
Dialog