HextaUI

Date picker

Un bouton qui ouvre un calendrier dans un popover, ou une bottom sheet sur mobile, pour des dates simples et des plages.

import { DatePicker } from "@/components/ui/date-picker"

export function DatePickerDemo() {
  return <DatePicker />
}
pnpm dlx shadcn@latest add https://hextaui.com/r/date-picker.json

Ajoute le composant, les tokens de thème HextaUI et les composants HextaUI dont il dépend.

import { DatePicker, DateRangePicker } from "@/components/ui/date-picker"
const [date, setDate] = React.useState<Date | null>(null)

<DatePicker value={date} onValueChange={setDate} />

Sur les écrans de moins de 640 px, le calendrier s’ouvre dans une bottom sheet au lieu d’un popover, pour que les jours restent assez grands pour être touchés.

Contrôlé

Passez value et onValueChange. Les valeurs effacées sont null. Le calendrier s’ouvre toujours sur le mois de la date sélectionnée, et choisir un jour le ferme et rend le focus au bouton.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { DatePicker } from "@/components/ui/date-picker"

function daysFromToday(days: number) {
  const date = new Date()
  date.setHours(0, 0, 0, 0)
  date.setDate(date.getDate() + days)
  return date
}

const isoDay = new Intl.DateTimeFormat("en-CA", {
  year: "numeric",
  month: "2-digit",
  day: "2-digit",
})

export function DatePickerControlled() {
  const [date, setDate] = React.useState<Date | null>(null)

  return (
    <div className="flex flex-wrap items-center justify-center gap-2">
      <DatePicker value={date} onValueChange={setDate} clearable />
      <Button variant="ghost" onClick={() => setDate(daysFromToday(0))}>
        Today
      </Button>
      <Button variant="ghost" onClick={() => setDate(daysFromToday(7))}>
        In a week
      </Button>
      <output className="text-sm text-muted-foreground tabular-nums">
        {date ? isoDay.format(date) : "null"}
      </output>
    </div>
  )
}

Plage

<DateRangePicker /> affiche deux mois sur les grands écrans. Le premier clic démarre toujours une nouvelle plage, même si une plage est définie, et le second la termine dans n’importe quel ordre et ferme le sélecteur.

"use client"

import * as React from "react"
import type { DateRange } from "react-day-picker"

import { DateRangePicker } from "@/components/ui/date-picker"

const isoDay = new Intl.DateTimeFormat("en-CA", {
  year: "numeric",
  month: "2-digit",
  day: "2-digit",
})

export function DatePickerRange() {
  const [range, setRange] = React.useState<DateRange | null>(null)

  return (
    <div className="flex flex-col items-center gap-2">
      <DateRangePicker value={range} onValueChange={setRange} clearable />
      <output className="text-sm text-muted-foreground tabular-nums">
        {range?.from && range.to
          ? `${isoDay.format(range.from)} → ${isoDay.format(range.to)}`
          : "null"}
      </output>
    </div>
  )
}

Jours désactivés

Passez n’importe quel matcher react-day-picker comme calendarProps.disabled. Ici, seuls les jours de semaine à venir peuvent être choisis.

"use client"

import * as React from "react"

import { DatePicker } from "@/components/ui/date-picker"

function tomorrow() {
  const date = new Date()
  date.setHours(0, 0, 0, 0)
  date.setDate(date.getDate() + 1)
  return date
}

export function DatePickerDisabledDays() {
  const [firstDay] = React.useState(tomorrow)

  return (
    <DatePicker
      placeholder="Delivery date"
      calendarProps={{
        disabled: [{ before: firstDay }, { dayOfWeek: [0, 6] }],
      }}
    />
  )
}

Listes déroulantes du mois et de l’année

Pour les dates éloignées comme une date de naissance, définissez captionLayout: "dropdown" avec un startMonth et un endMonth. Étiquetez le déclencheur avec un <label /> pointant vers son id.

import { DatePicker } from "@/components/ui/date-picker"

export function DatePickerDropdowns() {
  return (
    <div className="flex flex-col gap-1.5">
      <label htmlFor="birthday" className="text-sm font-medium">
        Date of birth
      </label>
      <DatePicker
        id="birthday"
        placeholder="Select your birthday"
        calendarProps={{
          captionLayout: "dropdown",
          startMonth: new Date(1920, 0),
          endMonth: new Date(),
          disabled: { after: new Date() },
        }}
      />
    </div>
  )
}

Dans un formulaire

Avec name, une date unique est soumise sous la forme 2026-10-14 et une plage sous la forme 2026-10-20/2026-10-24. Les sélecteurs vides soumettent une chaîne vide.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { DatePicker, DateRangePicker } from "@/components/ui/date-picker"

export function DatePickerForm() {
  const [submitted, setSubmitted] = React.useState<string>()

  return (
    <form
      className="flex flex-col items-start gap-3"
      onSubmit={(event) => {
        event.preventDefault()
        const data = new FormData(event.currentTarget)
        setSubmitted(JSON.stringify(Object.fromEntries(data)))
      }}
    >
      <DatePicker name="due" defaultValue={new Date(2026, 9, 14)} />
      <DateRangePicker
        name="stay"
        defaultValue={{
          from: new Date(2026, 9, 20),
          to: new Date(2026, 9, 24),
        }}
      />
      <Button type="submit">Submit</Button>
      <output className="text-sm break-all text-muted-foreground">
        {submitted ?? "Not submitted"}
      </output>
    </form>
  )
}

Désactivé

Un déclencheur désactivé peut quand même afficher une valeur.

import { DatePicker } from "@/components/ui/date-picker"

export function DatePickerDisabled() {
  return (
    <div className="flex flex-wrap justify-center gap-2">
      <DatePicker disabled />
      <DatePicker disabled defaultValue={new Date(2026, 0, 1)} />
    </div>
  )
}

Conteneur étroit

Le déclencheur ne dépasse jamais son conteneur. Les libellés longs sont tronqués au lieu de passer à la ligne.

import { DateRangePicker } from "@/components/ui/date-picker"

export function DatePickerNarrow() {
  return (
    <div className="w-40">
      <DateRangePicker
        defaultValue={{
          from: new Date(2026, 0, 28),
          to: new Date(2026, 11, 3),
        }}
      />
    </div>
  )
}

Dans une sheet

Le sélecteur se superpose à la sheet, et Escape ne ferme que le sélecteur.

import { Button } from "@/components/ui/button"
import { DateRangePicker } from "@/components/ui/date-picker"
import {
  Sheet,
  SheetBody,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"

export function DatePickerInSheet() {
  return (
    <Sheet>
      <SheetTrigger render={<Button variant="outline" />}>
        Edit booking
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Edit booking</SheetTitle>
          <SheetDescription>
            The picker opens above the sheet and Escape closes only the picker.
          </SheetDescription>
        </SheetHeader>
        <SheetBody>
          <DateRangePicker />
        </SheetBody>
      </SheetContent>
    </Sheet>
  )
}

De droite à gauche

Passez locale pour le libellé du déclencheur et une locale react-day-picker avec dir: "rtl" dans calendarProps pour le calendrier. Les flèches suivent la direction.

"use client"

import { arSA } from "react-day-picker/locale"

import { DatePicker, DateRangePicker } from "@/components/ui/date-picker"

export function DatePickerRtl() {
  return (
    <div dir="rtl" className="flex flex-wrap justify-center gap-2">
      <DatePicker
        locale="ar"
        placeholder="اختر تاريخًا"
        title="اختر تاريخًا"
        clearLabel="مسح"
        clearable
        defaultValue={new Date(2026, 9, 2)}
        calendarProps={{ locale: arSA, dir: "rtl" }}
      />
      <DateRangePicker
        locale="ar"
        placeholder="اختر فترة"
        title="اختر فترة"
        calendarProps={{ locale: arSA, dir: "rtl" }}
      />
    </div>
  )
}
ToucheAction
EnterSpaceSur le déclencheur, ouvre le sélecteur et donne le focus au jour sélectionné, ou à aujourd’hui. Sur un jour, le sélectionne.
←→Passe au jour précédent ou suivant.
↑↓Passe au même jour de la semaine précédente ou suivante.
Page UpPage DownPasse au mois précédent ou suivant.
ShiftPage UpPasse à l’année précédente. Shift Page Down passe à la suivante.
HomeEndPasse au début ou à la fin de la semaine.
EscFerme le sélecteur et rend le focus au déclencheur.
  • Le popover est étiqueté avec title, et la bottom sheet l’affiche comme titre. Traduisez-le avec placeholder.
  • Le déclencheur n’a pas de libellé visible propre. Associez-le à un <label /> ou passez aria-label.
  • Les jours désactivés sont ignorés par le clavier et annoncés comme indisponibles.

Les deux sélecteurs partagent les props ci-dessous et rendent un déclencheur <Button /> avec un <Calendar /> dans un popover ou une bottom sheet.

PropTypePar défaut
value
Date | null–
defaultValue
Date | nullnull
onValueChange
(value: Date | null) => void–
placeholder
string"Pick a date"
titleNom accessible du popover, titre de la sheet.
string"Select a date"
open
boolean–
defaultOpen
booleanfalse
onOpenChange
(open: boolean) => void–
localeFormate le libellé du déclencheur avec Intl.DateTimeFormat. Passez une locale react-day-picker dans calendarProps pour traduire aussi le calendrier.
string"en-US"
clearableAjoute un bouton Clear sous le calendrier dès qu’il y a une valeur.
booleanfalse
clearLabel
string"Clear"
nameRend un champ masqué pour que la valeur soit soumise avec son formulaire.
string–
calendarPropsTransmis au calendrier, sauf mode, selected, onSelect, required, numberOfMonths et autoFocus. Utilisez-le pour les jours désactivés, captionLayout, startMonth, endMonth, locale et dir.
CalendarProps–
variantLe déclencheur est un Button et accepte aussi ses autres props.
Button variant"outline"
disabled
booleanfalse
classNameAppliqué au déclencheur, qui est w-60 par défaut.
string–
PropTypePar défaut
value
{ from: Date; to?: Date } | null–
defaultValue
{ from: Date; to?: Date } | nullnull
onValueChangeAppelé une fois le second jour choisi.
(value: DateRange | null) => void–
placeholder
string"Pick a date range"
title
string"Select dates"
open
boolean–
defaultOpen
booleanfalse
onOpenChange
(open: boolean) => void–
localeFormate le libellé du déclencheur avec Intl.DateTimeFormat. Passez une locale react-day-picker dans calendarProps pour traduire aussi le calendrier.
string"en-US"
clearableAjoute un bouton Clear sous le calendrier dès qu’il y a une valeur.
booleanfalse
clearLabel
string"Clear"
nameRend un champ masqué pour que la valeur soit soumise avec son formulaire.
string–
calendarPropsTransmis au calendrier, sauf mode, selected, onSelect, required, numberOfMonths et autoFocus. Utilisez-le pour les jours désactivés, captionLayout, startMonth, endMonth, locale et dir.
CalendarProps–
variantLe déclencheur est un Button et accepte aussi ses autres props.
Button variant"outline"
disabled
booleanfalse
classNameAppliqué au déclencheur, qui est w-60 par défaut.
string–
AttributDescription
data-slot="date-picker-trigger"Le bouton déclencheur.
data-emptyPrésent sur le déclencheur tant qu’il n’y a pas de valeur. Affiche le placeholder en texte atténué.
data-popup-openPrésent sur le déclencheur tant que le sélecteur est ouvert.
data-slot="date-picker-content"Enveloppe le calendrier dans le popover ou la sheet.
data-slot="date-picker-clear"Le bouton Clear.

Utilisé dans les blocks

Des blocks qui s’appuient sur Date picker.