Calendar
Calendário embutido na tela para escolher uma data (Calendar) ou um período (RangeCalendar).
setembro de 2026
| D | S | T | Q | Q | S | S |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
'use client';
import { getLocalTimeZone, today } from '@internationalized/date';
import { I18nProvider } from 'react-aria-components';
import { Calendar } from '@/components/ui/calendar';
export default function CalendarDemo() {
return (
<I18nProvider locale="pt-BR">
<Calendar
aria-label="Data do agendamento"
defaultValue={today(getLocalTimeZone())}
className="rounded-md border bg-card"
/>
</I18nProvider>
);
}Instalação
npx shadcn@latest add @lenstech/calendarUso
import { getLocalTimeZone, today } from "@internationalized/date"
import { I18nProvider } from "react-aria-components"
import { Calendar } from "@/components/ui/calendar"<I18nProvider locale="pt-BR">
<Calendar aria-label="Data do agendamento" defaultValue={today(getLocalTimeZone())} />
</I18nProvider>Datas são CalendarDate, não Date
O valor do calendário é um objeto do pacote @internationalized/date (CalendarDate), sem hora nem fuso —
assim "15/10" nunca vira "14/10" por causa do fuso horário.
- Criar:
today(getLocalTimeZone()),parseDate("2026-10-15")ounew CalendarDate(2026, 10, 15). - Enviar para a API:
data.toString()→"2026-10-15"(ISO). - Converter para
Date:data.toDate(getLocalTimeZone()). - Converter de
Date:parseDate(date.toISOString().slice(0, 10)). - Calcular:
data.add({ days: 7 }),data.subtract({ months: 1 }),data.compare(outra).
Idioma
Envolva em <I18nProvider locale="pt-BR"> (ou defina o idioma na raiz da aplicação) para ter nomes de meses,
dias da semana e início da semana em português.
Exemplos
Período (RangeCalendar)
RangeCalendar seleciona início e fim. O valor é { start, end }; os extremos ficam em primary e o meio em accent.
setembro de 2026
| D | S | T | Q | Q | S | S |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
De 24 de set. a 30 de set.
'use client';
import * as React from 'react';
import { getLocalTimeZone, today } from '@internationalized/date';
import { I18nProvider, type DateRange } from 'react-aria-components';
import { RangeCalendar } from '@/components/ui/calendar';
const formatter = new Intl.DateTimeFormat('pt-BR', { day: '2-digit', month: 'short' });
export default function CalendarRange() {
const now = today(getLocalTimeZone());
const [range, setRange] = React.useState<DateRange | null>({ start: now.subtract({ days: 6 }), end: now });
return (
<I18nProvider locale="pt-BR">
<div className="flex flex-col items-center gap-3">
<RangeCalendar
aria-label="Período do relatório"
value={range}
onChange={setRange}
className="rounded-md border bg-card"
/>
<p className="text-sm text-muted-foreground" aria-live="polite">
{range
? `De ${formatter.format(range.start.toDate(getLocalTimeZone()))} a ${formatter.format(range.end.toDate(getLocalTimeZone()))}`
: 'Nenhum período selecionado'}
</p>
</div>
</I18nProvider>
);
}Datas mínima, máxima e indisponíveis
minValue e maxValue limitam a navegação; isDateUnavailable risca dias específicos (fins de semana, feriados, agenda cheia).
setembro de 2026
| D | S | T | Q | Q | S | S |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
'use client';
import { getLocalTimeZone, isWeekend, today, type DateValue } from '@internationalized/date';
import { I18nProvider } from 'react-aria-components';
import { Calendar } from '@/components/ui/calendar';
export default function CalendarUnavailable() {
const now = today(getLocalTimeZone());
// Feriado fixo de exemplo: 15 de novembro (Proclamação da República).
const isHoliday = (date: DateValue) => date.month === 11 && date.day === 15;
return (
<I18nProvider locale="pt-BR">
<Calendar
aria-label="Data da visita técnica"
minValue={now}
maxValue={now.add({ months: 2 })}
isDateUnavailable={(date) => isWeekend(date, 'pt-BR') || isHoliday(date)}
className="rounded-md border bg-card"
/>
</I18nProvider>
);
}Controlado
Use value e onChange para ler a data escolhida e reagir a ela.
outubro de 2026
| D | S | T | Q | Q | S | S |
|---|---|---|---|---|---|---|
27 | 28 | 29 | 30 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Entrega em sábado, 3 de outubro de 2026
'use client';
import * as React from 'react';
import { getLocalTimeZone, today, type CalendarDate } from '@internationalized/date';
import { I18nProvider } from 'react-aria-components';
import { Calendar } from '@/components/ui/calendar';
const formatter = new Intl.DateTimeFormat('pt-BR', { dateStyle: 'full' });
export default function CalendarControlled() {
const [date, setDate] = React.useState<CalendarDate>(today(getLocalTimeZone()).add({ days: 3 }));
return (
<I18nProvider locale="pt-BR">
<div className="flex flex-col items-center gap-3">
<Calendar aria-label="Data de entrega" value={date} onChange={setDate} className="rounded-md border bg-card" />
<p className="text-sm text-muted-foreground" aria-live="polite">
Entrega em <span className="font-medium text-foreground">{formatter.format(date.toDate(getLocalTimeZone()))}</span>
</p>
</div>
</I18nProvider>
);
}Inválido
Com isInvalid e errorMessage, a mensagem aparece abaixo do calendário.
setembro de 2026
| D | S | T | Q | Q | S | S |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
'use client';
import { getLocalTimeZone, today } from '@internationalized/date';
import { I18nProvider } from 'react-aria-components';
import { Calendar } from '@/components/ui/calendar';
export default function CalendarInvalid() {
return (
<I18nProvider locale="pt-BR">
<Calendar
aria-label="Data do evento"
isInvalid
defaultValue={today(getLocalTimeZone()).subtract({ days: 2 })}
errorMessage="Escolha uma data a partir de hoje."
className="rounded-md border bg-card"
/>
</I18nProvider>
);
}Quando usar
- Use embutido na tela, quando o calendário é o foco: agenda de atendimentos, escolha de dia de entrega.
- Não use em formulários comuns — prefira
Date Picker, que permite digitar e abre o calendário num popover. - Não use para datas conhecidas de cor, como nascimento — use
Date Field.
API
Calendar
Aceita todas as props do Calendar do React Aria, mais:
Prop
Type
RangeCalendar
Aceita todas as props do RangeCalendar do React Aria, mais errorMessage.
value, defaultValue e onChange usam { start: DateValue; end: DateValue }.