HextaUI

Date picker

Um botão que abre um calendário em um popover, ou em um bottom sheet no celular, para datas únicas e intervalos.

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

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

Adiciona o componente, os tokens de tema do HextaUI e quaisquer componentes do HextaUI dos quais ele depende.

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

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

Em telas com menos de 640px de largura, o calendário abre em um bottom sheet em vez de um popover, para que os dias continuem grandes o bastante para o toque.

Controlado

Passe value e onValueChange. Valores limpos são null. O calendário sempre abre no mês da data selecionada, e escolher um dia o fecha e devolve o foco ao botão.

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

Intervalo

<DateRangePicker /> mostra dois meses em telas maiores. O primeiro clique sempre inicia um novo intervalo, mesmo quando já há um definido, e o segundo clique o conclui em qualquer ordem e fecha o seletor.

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

Dias desativados

Passe qualquer matcher do react-day-picker como calendarProps.disabled. Aqui, apenas dias úteis futuros podem ser escolhidos.

"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] }],
      }}
    />
  )
}

Dropdowns de mês e ano

Para datas distantes, como um aniversário, defina captionLayout: "dropdown" com um startMonth e um endMonth. Rotule o gatilho com um <label /> apontando para seu 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>
  )
}

Em um formulário

Com name, uma data única é enviada como 2026-10-14 e um intervalo como 2026-10-20/2026-10-24. Seletores vazios enviam uma string vazia.

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

Desabilitado

Um gatilho desativado ainda pode exibir um valor.

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

Contêiner estreito

O gatilho nunca cresce além do seu contêiner. Rótulos longos são truncados em vez de quebrar de linha.

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

Dentro de uma sheet

O seletor fica acima do sheet, e Escape fecha apenas o seletor.

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

Da direita para a esquerda

Passe locale para o rótulo do gatilho e um locale do react-day-picker com dir: "rtl" em calendarProps para o calendário. As teclas de seta seguem a direção.

"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>
  )
}
TeclaAção
EnterSpaceNo gatilho, abre o seletor e foca o dia selecionado, ou hoje. Em um dia, o seleciona.
←→Move para o dia anterior ou seguinte.
↑↓Move para o mesmo dia da semana anterior ou seguinte.
Page UpPage DownMove para o mês anterior ou seguinte.
ShiftPage UpMove para o ano anterior. Shift Page Down move para o seguinte.
HomeEndMove para o início ou o fim da semana.
EscFecha o seletor e devolve o foco ao gatilho.
  • O popover é rotulado com title, e o bottom sheet o exibe como heading. Traduza-o junto com placeholder.
  • O gatilho não tem um rótulo visível próprio. Combine-o com um <label /> ou passe aria-label.
  • Dias desativados são ignorados pelo teclado e anunciados como indisponíveis.

Os dois seletores compartilham as props abaixo e renderizam um gatilho <Button /> com um <Calendar /> em um popover ou bottom sheet.

PropTipoPadrão
value
Date | null–
defaultValue
Date | nullnull
onValueChange
(value: Date | null) => void–
placeholder
string"Pick a date"
titleNome acessível do popover, título do sheet.
string"Select a date"
open
boolean–
defaultOpen
booleanfalse
onOpenChange
(open: boolean) => void–
localeFormata o rótulo do gatilho com Intl.DateTimeFormat. Passe um locale do react-day-picker em calendarProps para traduzir o calendário também.
string"en-US"
clearableAdiciona um botão Clear abaixo do calendário quando há um valor.
booleanfalse
clearLabel
string"Clear"
nameRenderiza um input oculto para que o valor seja enviado com o formulário.
string–
calendarPropsRepassado ao calendário, exceto mode, selected, onSelect, required, numberOfMonths e autoFocus. Use para dias desativados, captionLayout, startMonth, endMonth, locale e dir.
CalendarProps–
variantO gatilho é um Button e aceita também suas outras props.
Button variant"outline"
disabled
booleanfalse
classNameAplicado ao gatilho, que é w-60 por padrão.
string–
PropTipoPadrão
value
{ from: Date; to?: Date } | null–
defaultValue
{ from: Date; to?: Date } | nullnull
onValueChangeChamado quando o segundo dia é escolhido.
(value: DateRange | null) => void–
placeholder
string"Pick a date range"
title
string"Select dates"
open
boolean–
defaultOpen
booleanfalse
onOpenChange
(open: boolean) => void–
localeFormata o rótulo do gatilho com Intl.DateTimeFormat. Passe um locale do react-day-picker em calendarProps para traduzir o calendário também.
string"en-US"
clearableAdiciona um botão Clear abaixo do calendário quando há um valor.
booleanfalse
clearLabel
string"Clear"
nameRenderiza um input oculto para que o valor seja enviado com o formulário.
string–
calendarPropsRepassado ao calendário, exceto mode, selected, onSelect, required, numberOfMonths e autoFocus. Use para dias desativados, captionLayout, startMonth, endMonth, locale e dir.
CalendarProps–
variantO gatilho é um Button e aceita também suas outras props.
Button variant"outline"
disabled
booleanfalse
classNameAplicado ao gatilho, que é w-60 por padrão.
string–
AtributoDescrição
data-slot="date-picker-trigger"O botão gatilho.
data-emptyPresente no gatilho enquanto não há valor. Mostra o placeholder em texto atenuado.
data-popup-openPresente no gatilho enquanto o seletor está aberto.
data-slot="date-picker-content"Envolve o calendário no popover ou sheet.
data-slot="date-picker-clear"O botão Clear.

Usado em blocos

Blocos que se baseiam em Date picker.