Date Range Picker

A component that allows users to select a date range from a calendar.

Anatomy

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.

Examples

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>
  )
}

Standalone Date Range Picker

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>
  )
}

API Reference

Root

PropTypeDefault
asChild
boolean
closeOnSelect
booleantrue
defaultValue
string[]
dir
'ltr' | 'rtl'"ltr"
disabled
boolean
fixedWeeks
boolean
focusedValue
string
format
(date: DateValue[]) => string
getRootNode
() => Node | ShadowRoot | Document
id
string
ids
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
(date: DateValue, locale: string) => boolean
lazyMount
booleanfalse
locale
string
max
DateValue
min
DateValue
modal
boolean
name
string
numOfMonths
number
onExitComplete
() => void
onFocusChange
(details: FocusChangeDetails) => void
onOpenChange
(details: OpenChangeDetails) => void
onValueChange
(details: ValueChangeDetails) => void
onViewChange
(details: ViewChangeDetails) => void
open
boolean
parse
(value: string) => DateValue[]
positioning
PositioningOptions
present
boolean
readOnly
boolean
selectionMode
SelectionMode
startOfWeek
number
timeZone
string
translations
IntlTranslations
unmountOnExit
booleanfalse
value
string[]
view
DateView"day"

View

PropTypeDefault
view
DateView
asChild
boolean

Input

PropTypeDefault
asChild
boolean

Label

PropTypeDefault
asChild
boolean

Table

PropTypeDefault
asChild
boolean
columns
number

Content

PropTypeDefault
asChild
boolean

Control

PropTypeDefault
asChild
boolean

Trigger

PropTypeDefault
asChild
boolean

TableRow

PropTypeDefault
asChild
boolean

RangeText

PropTypeDefault
asChild
boolean

TableBody

PropTypeDefault
asChild
boolean

TableCell

PropTypeDefault
value
number | DateValue
asChild
boolean
columns
number
disabled
boolean
visibleRange
VisibleRange

TableHead

PropTypeDefault
asChild
boolean

Positioner

PropTypeDefault
asChild
boolean

YearSelect

PropTypeDefault
asChild
boolean

MonthSelect

PropTypeDefault
asChild
boolean

NextTrigger

PropTypeDefault
asChild
boolean

PrevTrigger

PropTypeDefault
asChild
boolean

TableHeader

PropTypeDefault
asChild
boolean

ViewControl

PropTypeDefault
asChild
boolean

ViewTrigger

PropTypeDefault
asChild
boolean

ClearTrigger

PropTypeDefault
asChild
boolean

TableCellTrigger

PropTypeDefault
asChild
boolean