# shadcn calendar and date picker: dates, state and time zones

The shadcn Calendar is react-day-picker inside a Popover: keep the date as a YYYY-MM-DD string and turn it into a Date only in the interface.

> Page: https://ui.eneawork.it/blog/shadcn-calendar-date-picker
> Markdown: https://ui.eneawork.it/blog/shadcn-calendar-date-picker.md

*2026-11-01*

Risposta breve: il Calendar di shadcn è react-day-picker avvolto in un componente che possiedi, e il date picker è quel calendario dentro un Popover. Il valore da salvare è una stringa YYYY-MM-DD, non un Date.

react-day-picker, la dipendenza che shadcn avvolge, è alla versione 10.0.2 nel registro npm, verificata il 1 novembre 2026. L'indice dei blocchi pubblicato contiene 1.556 blocchi, 2 dei quali con «calendar» nel nome, contati il 1 novembre 2026 nel file sitemap-blocks.xml. Il catalogo EneUI misura 111.985 componenti, 1.556 blocchi e 12.808 icone da 1.472 librerie, letti da /api/stats il 1 novembre 2026.

## Cosa aggiunge il comando

Il CLI copia calendar.tsx e popover.tsx in components/ui, aggiunge react-day-picker e date-fns alle dipendenze e non tocca altro. Da quel momento il file è tuo: gli aggiornamenti di shadcn non arrivano più.

npx shadcn@latest add calendar popover button

## Il campo, non solo la griglia

Un calendario da solo non è un campo: manca il valore leggibile, il placeholder e il legame con il form. Il pattern è un Button in variante outline che mostra la data formattata e apre il calendario.

"use client"; import { useState } from "react"; import { format } from "date-fns"; import { Button } from "@/components/ui/button"; import { Calendar } from "@/components/ui/calendar"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; export function DateField({ name, label }: { name: string; label: string }) { const [value, setValue] = useState(""); // Local midnight, not UTC midnight: the string is a day, not an instant. const selected = value ? new Date(value + "T00:00:00") : undefined; return ( <> {selected ? format(selected, "PPP") : "Scegli una data"} setValue(day ? format(day, "yyyy-MM-dd") : "")} /> ); }

## La stringa, non un oggetto Date

Il valore che attraversa il form è una stringa: 2026-11-01 è lo stesso giorno a Milano, a New York e sul server. Un Date invece porta con sé un istante, e l'istante dipende dal fuso di chi lo crea.

## Le tre varianti che coprono quasi tutto

Fonte: la pagina Calendar di shadcn/ui (licenza MIT) e il repository react-day-picker (licenza MIT); i formati PPP e yyyy-MM-dd sono di date-fns, anch'esso MIT.

## Limite dichiarato

Il calendario disegna i giorni nel fuso del browser, il server non ne ha uno. Finché il valore resta una stringa i due coincidono; appena scrivi new Date("2026-11-01") su un server a ovest di Greenwich, JavaScript legge mezzanotte UTC e il giorno torna indietro di uno. Secondo limite: i giorni disabilitati sono interfaccia, non una regola. Un utente può inviare la domenica che hai chiuso, quindi la validazione va rifatta sul server.

## Dove continuare

Nel catalogo trovi la libreria shadcn, un blocco calendario con anteprima e i blocchi della categoria form. Per capire cosa stai installando leggi che cos'è shadcn/ui e come tematizzare shadcn; il confronto tra temi pronti è temi shadcn gratis o premium. Se la data arriva dal server e React si lamenta, la causa è in hydration mismatch sulle date; per le liste lunghe il fratello di questo articolo è select con ricerca.

## FAQ

### Serve react-day-picker per usare il Calendar?

Sì. `calendar.tsx` è un wrapper di react-day-picker (10.0.2 al 1 novembre 2026) con classi Tailwind: senza quella dipendenza il file non compila.

### La data salvata è sbagliata di un giorno. Perché?

Quasi sempre per `new Date("2026-11-01")`, che JavaScript legge come mezzanotte UTC. Passa un'ora locale esplicita: `new Date("2026-11-01T00:00:00")`.

### Come faccio un intervallo di date?

Con `mode="range"`: `onSelect` restituisce un oggetto con `from` e `to`. Su mobile tieni un mese solo, su desktop due mesi affiancati con `numberOfMonths={2}`.

### Il popover si chiude quando clicco un giorno: è un bug?

No, è il comportamento atteso: il click seleziona e il popover perde il focus. Se vuoi tenerlo aperto, controlla `open` e ignora `onOpenChange` quando la selezione è valida.

### Posso usare il calendario dentro un form non controllato?

Sì, con un input nascosto che porta il `name`: il valore inviato è la stringa formattata, non l'oggetto `Date`.
