shadcn/ui içinde tarih seçmenin iki parçası vardır: Calendar bileşeni ekranda bir ay görünümü çizer ve gün seçtirir, Date Picker ise ayrı bir bileşen değil, Popover ile Calendar bileşenlerinin birleştirildiği bir kalıptır. Kurulum için npx shadcn@latest add calendar komutu yeterlidir; tarih seçici için buna ek olarak popover bileşenini de eklersiniz. Takvimin bütün davranışı React DayPicker kütüphanesinden gelir, shadcn/ui bu kütüphanenin üstüne kendi tema renklerini ve buton stillerini giydirir.
Bu yazıda resmî Calendar ve Date Picker sayfalarını, depodaki calendar.tsx kaynağını ve belgede gösterilen örnek dosyaları birlikte okuyoruz: tek gün ve aralık seçimi, ay ve yıl açılır listeleri, saat dilimi ayarı, hücre boyutu, Farsça takvim, doğum tarihi seçici, metin kutulu seçici ve yükseltme adımları. Diğer katmanlı bileşenlerin genel mantığını merak ediyorsanız Popover, Tooltip ve Hover Card yazısına, serinin bütününe ise shadcn/ui öğrenme rehberi sayfasından ulaşabilirsiniz.
Calendar bileşeni neye dayanır?
Belgeye göre Calendar, React DayPicker belgeleri üzerine kurulmuştur. Bileşen dosyası DayPicker'ın DayPicker bileşenini sarar, ona shadcn/ui sınıflarını verir ve ileri/geri okları, açılır listeler ve gün düğmeleri gibi parçaları kendi bileşenleriyle değiştirir. Bu yüzden shadcn/ui belgesi ayrıntılı seçenekler için sık sık DayPicker belgesine yönlendirir: hangi prop'un ne yaptığını, hangi seçim modlarının olduğunu ve özelleştirmenin nasıl yapıldığını orada bulursunuz.
Değişiklik günlüğünde Haziran 2025 başlığı altında takvimin DayPicker'ın son sürümüne yükseltildiği, bunun büyük bir yükseltme olduğu ve aynı anda 30'dan fazla takvim bloğu hazırlandığı yazar. Bu bloklar sitenin Blocks kütüphanesinde durur ve her biri calendar-02 gibi bir adla doğrudan projeye eklenebilir. Belgede örnek olarak gösterilen aralık takvimi, ay ve yıl seçici, doğum tarihi seçici ve saatli seçici de aslında bu bloklardır.
Kurulum
CLI ile kurulum tek satırdır. Komut components/ui/calendar.tsx dosyasını oluşturur; depodaki kayıt tanımına göre bağımlılık olarak react-day-picker ve date-fns paketlerini, kayıt bağımlılığı olarak da button bileşenini getirir. Başka bir paket yöneticisi kullanıyorsanız komutun o yöneticideki karşılığını çalıştırırsınız.
npx shadcn@latest add calendarElle kurulumda önce iki paketi yüklersiniz, ardından projenizde Button bileşeninin olduğundan emin olursunuz, çünkü takvimin gün düğmeleri ve ok düğmeleri bu bileşeni kullanır. Son adım bileşen kaynağını components/ui/calendar.tsx dosyasına kopyalamak ve import yollarını kendi yapınıza göre düzeltmektir. CLI'ın genel kullanımı için shadcn CLI komutları yazısına bakabilirsiniz.
npm install react-day-picker date-fnsTemel kullanım: tek gün seçimi
En basit kullanımda seçilen tarihi bir React durumunda tutarsınız. mode="single" tek gün seçimini açar, selected o anki değeri verir, onSelect ise kullanıcı bir güne tıkladığında yeni değeri döndürür. Durumun tipi Date | undefined olarak tanımlanır, çünkü seçili gün kaldırıldığında değer boş kalabilir.
"use client"
import * as React from "react"
import { Calendar } from "@/components/ui/calendar"
export function TarihSec() {
const [date, setDate] = React.useState<Date | undefined>(new Date())
return (
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-lg border"
/>
)
}Bu kod bugünün tarihini seçili olarak başlatır ve kenarlıklı, köşeleri yuvarlatılmış bir takvim çizer. Bileşen dosyası en üstte "use client" satırını taşıdığı için Next.js App Router projelerinde istemci bileşeni olarak çalışır; durum kullanan kendi sarmalayıcınızın da istemci tarafında olması gerekir. Bu konunun Next.js tarafı shadcn/ui Next.js kurulumu yazısında anlatılıyor.
calendar.tsx dosyasının içinde ne var?

shadcn/ui'ın temel fikri, bileşen kodunun projenize kopyalanması ve sizin olmasıdır. Takvimi özelleştirmeden önce dosyanın ne yaptığını bilmek işinizi kolaylaştırır. Bileşen, DayPicker'ın tüm prop'larını kabul eder ve birkaç tanesine kendi varsayılanını koyar:
| Prop | Varsayılan | Görevi |
|---|---|---|
| showOutsideDays | true | Ayın dışındaki günleri soluk renkte gösterir |
| captionLayout | "label" | Başlıkta ay adını düz metin olarak gösterir; açılır liste için "dropdown" değerleri kullanılır |
| buttonVariant | "ghost" | Ok düğmelerinin Button varyantı; Button bileşenindeki variant değerlerini alır |
| formatters | formatMonthDropdown tanımlı | Ay açılır listesinde ayları kısa adla yazar; kendi formatter'larınız bunun üstüne eklenir |
| classNames | shadcn sınıfları | Verdiğiniz sınıflar varsayılan sınıflarla birleştirilir |
| components | Root, Chevron, DayButton, WeekNumber | DayPicker parçalarını shadcn bileşenleriyle değiştirir; kendi parçalarınız bunları ezer |
Kök öğe data-slot="calendar" özniteliğini taşır, böylece üst bileşenlerden bu öğeyi seçebilirsiniz. Kök sınıfında dikkat çekici bir ayrıntı da vardır: takvim bir kartın içerik alanına (data-slot=card-content) ya da bir popover içeriğine (data-slot=popover-content) yerleştirildiğinde arka planı şeffaf olur. Böylece takvim, içine konduğu kutunun rengini alır ve iki farklı arka plan üst üste görünmez.
Gün düğmeleri CalendarDayButton adlı ayrı bir bileşendir ve ghost varyantlı, icon boyutlu bir Button çizer. Düğme seçim durumunu data-selected-single, data-range-start, data-range-middle ve data-range-end öznitelikleriyle bildirir; renkler de bu özniteliklere bağlı sınıflarla verilir. Tek seçili gün ve aralığın uçları primary rengini, aralığın ortası accent rengini kullanır. Tema renklerinin nereden geldiğini shadcn/ui tema ve CSS değişkenleri yazısında bulabilirsiniz; className ile sınıf birleştirmenin mantığı ise cn() fonksiyonu yazısında.
Aralık seçimi

Bir tatil ya da rapor dönemi gibi başlangıç ve bitişi olan bir aralık seçtirecekseniz mode="range" kullanırsınız. Bu modda değer tek bir Date değil, from ve to alanları olan bir DateRange nesnesidir; tip doğrudan react-day-picker paketinden alınır. Belgedeki aralık örneği numberOfMonths={2} ile iki ayı yan yana gösterir ve defaultMonth ile takvimi aralığın başladığı ayda açar.
"use client"
import * as React from "react"
import { type DateRange } from "react-day-picker"
import { Calendar } from "@/components/ui/calendar"
export function TatilAraligi() {
const [dateRange, setDateRange] = React.useState<DateRange | undefined>({
from: new Date(2025, 5, 12),
to: new Date(2025, 6, 15),
})
return (
<Calendar
mode="range"
defaultMonth={dateRange?.from}
selected={dateRange}
onSelect={setDateRange}
numberOfMonths={2}
className="rounded-lg border shadow-sm"
/>
)
}JavaScript'te Date yapıcısının ay parametresi sıfırdan başlar; bu yüzden new Date(2025, 5, 12) 12 Haziran 2025 anlamına gelir. Aralık örneğini kendi verinize uyarlarken en sık karışan nokta budur. Bileşenin kaynağında aralığın ilk ve son gününe yuvarlatılmış köşe, ortadaki günlere düz köşe verildiğini görürsünüz; bu yüzden seçili aralık ekranda tek parça bir şerit gibi görünür.
Ay ve yıl açılır listeleri
Doğum tarihi gibi yıllar öncesine gitmesi gereken seçimlerde ok düğmeleriyle ay ay geri gitmek yorucu olur. captionLayout prop'u başlığı açılır listeye çevirir. Belgedeki ay ve yıl seçici bloğu üç değeri bir Select ile değiştirerek gösterir: "dropdown" ay ve yıl listesini birlikte, "dropdown-months" yalnızca ay listesini, "dropdown-years" ise yalnızca yıl listesini getirir. Varsayılan değer olan "label" başlığı düz metin bırakır.
<Calendar
mode="single"
defaultMonth={date}
selected={date}
onSelect={setDate}
captionLayout="dropdown"
className="rounded-lg border shadow-sm"
/>Bileşen kaynağı bu listeleri de tema ile uyumlu hale getirir: liste kökü kenarlıklı bir kutu olarak çizilir, asıl liste öğesi ise absolute inset-0 opacity-0 sınıflarıyla görünmez biçimde onun üstüne yerleştirilir. Böylece tıklama asıl listeye gider ama görünüm shadcn/ui stilinde kalır.
Saat dilimi ve kaymış tarih sorunu
Belgede ayrı bir bölüm, seçilen günün bir gün kaymış görünmesi sorununa ayrılmıştır: örneğin ayın 20'sini seçtiğinizde 19'u işaretleniyorsa, takvime kullanıcının yerel saat dilimini vermeniz önerilir. Calendar bunun için timeZone prop'unu kabul eder.
export function CalendarWithTimezone() {
const [date, setDate] = React.useState<Date | undefined>(undefined)
const [timeZone, setTimeZone] = React.useState<string | undefined>(undefined)
React.useEffect(() => {
setTimeZone(Intl.DateTimeFormat().resolvedOptions().timeZone)
}, [])
return (
<Calendar
mode="single"
selected={date}
onSelect={setDate}
timeZone={timeZone}
/>
)
}Saat diliminin neden useEffect içinde bulunduğu da belgede açıklanır: sunucu tarafında oluşturulan sayfalarda sunucu ile kullanıcının tarayıcısı farklı saat dilimlerinde olabilir. Saat dilimi doğrudan render sırasında okunursa sunucunun ürettiği HTML ile istemcinin ürettiği çıktı birbirini tutmaz ve hydration uyuşmazlığı doğar. Değeri efekt içinde, yani yalnızca tarayıcıda okumak bu sorunu ortadan kaldırır.
Hücre boyutunu değiştirmek
Takvimdeki bütün hücreler ve ok düğmeleri tek bir CSS değişkeninden, --cell-size değerinden boyut alır. Kaynakta bu değer --spacing(8) olarak tanımlıdır. Daha büyük ya da ekran genişliğine göre değişen hücreler istiyorsanız bu değişkeni className ile ezersiniz. Belge hem Tailwind boşluk ölçeğiyle hem de sabit rem değerleriyle birer örnek verir:
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-lg border [--cell-size:--spacing(11)] md:[--cell-size:--spacing(12)]"
/>CSS değişkenlerinin genel çalışma biçimi bu serinin konusu değil; temel bilgi için CSS değişkenleri rehberine bakabilirsiniz. Burada bilmeniz gereken tek şey, takvimdeki boyutların tek noktadan yönetildiği ve bileşen dosyasına dokunmadan değiştirilebildiğidir.
Farsça (Hicri / Celali) takvim
Belge, Farsça takvim için çok kısa bir yol gösterir: components/ui/calendar.tsx dosyasında DayPicker içe aktarmasını react-day-picker yerine react-day-picker/persian yolundan yapmanız yeterlidir. Depodaki calendar-hijri örneği tam olarak bunu yapar ve geri kalan bileşen kodunu aynen korur.
// components/ui/calendar.tsx
import { DayPicker } from "react-day-picker/persian"Bu değişiklik dosyanın tamamını etkiler, yani projedeki bütün takvimler Farsça takvime geçer. Hem Miladi hem Farsça takvim gerekiyorsa, örnek dosyanın yaptığı gibi ikinci bir bileşen tanımlamanız gerekir.
Date Picker: Popover ve Calendar birleşimi

shadcn/ui: Date Picker sayfası, tarih seçicinin bir Popover içine yerleştirilmiş Calendar olduğunu açıkça yazar ve kurulum için iki bileşenin sayfalarına yönlendirir. Tetikleyici olarak outline varyantlı bir buton kullanılır, popover içeriğinin genişliği w-auto ve iç boşluğu p-0 yapılır ki takvim kutuya tam otursun.
"use client"
import * as React from "react"
import { format } from "date-fns"
import { Calendar as CalendarIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
export function DatePickerDemo() {
const [date, setDate] = React.useState<Date>()
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
data-empty={!date}
className="data-[empty=true]:text-muted-foreground w-[280px] justify-start text-left font-normal"
>
<CalendarIcon />
{date ? format(date, "PPP") : <span>Tarih seçin</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar mode="single" selected={date} onSelect={setDate} />
</PopoverContent>
</Popover>
)
}Burada iki küçük teknik var. Birincisi, data-empty özniteliği tarih seçilmediğinde true olur ve data-[empty=true]:text-muted-foreground sınıfı yer tutucu metni soluk gösterir. İkincisi, seçili tarih date-fns paketinin format fonksiyonuyla "PPP" biçiminde yazdırılır. <PopoverTrigger asChild> kullanımı, tetikleyicinin ayrı bir düğme üretmek yerine içindeki Button bileşenine davranış aktarmasını sağlar; bu desenin ayrıntısı Button varyantları yazısında.
Seçince kapanan doğum tarihi seçici
Yukarıdaki örnekte popover, gün seçildikten sonra açık kalır. Doğum tarihi seçici bloğu bunu kontrollü bir popover ile çözer: open durumu bir değişkende tutulur ve onSelect içinde hem tarih kaydedilir hem de setOpen(false) çağrılır. Aynı blok captionLayout="dropdown" ile yıl listesini de açar.
const [open, setOpen] = React.useState(false)
const [date, setDate] = React.useState<Date | undefined>(undefined)
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button variant="outline" id="date" className="w-48 justify-between font-normal">
{date ? date.toLocaleDateString() : "Tarih seçin"}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto overflow-hidden p-0" align="start">
<Calendar
mode="single"
selected={date}
captionLayout="dropdown"
onSelect={(date) => {
setDate(date)
setOpen(false)
}}
/>
</PopoverContent>
</Popover>
)Metin kutulu, saatli ve hazır seçenekli varyasyonlar
Belgede gösterilen diğer bloklar aynı iskeleti genişletir. Metin kutulu seçici (calendar-28) bir Input ile takvimi yan yana kullanır: kullanıcı tarihi yazarsa geçerli olup olmadığı kontrol edilir, geçerliyse hem seçili tarih hem de takvimin gösterdiği ay güncellenir. Bunun için takvime month ve onMonthChange prop'ları verilir; aşağı ok tuşuna basıldığında popover açılır. Saatli seçici (calendar-24) tarih için popover'lı takvimi, saat için de type="time" özellikli ayrı bir Input kullanır; yani saat seçimi takvimin değil, yerel saat alanının işidir.
Doğal dil seçicisi (calendar-29), belgeye göre chrono-node kütüphanesiyle yazılan ifadeyi tarihe çevirir. Hazır seçenekli örnek (date-picker-with-presets) ise popover içine bir Select koyar ve bugün, yarın, üç gün sonra, bir hafta sonra gibi değerleri date-fns paketinin addDays fonksiyonuyla hesaplar. Bu örneklerin hepsi sizin projenize kopyalanan sıradan bileşen kodu olduğu için ihtiyacınıza göre parçalarını alıp birleştirebilirsiniz. Formlarda tarih alanını doğrulamak için React Hook Form ve Zod yazısına bakabilirsiniz.
Yükseltme rehberi
Eski bir takvim bileşeniniz varsa belge iki yol tarif eder. Tailwind v4 kullanıyorsanız npx shadcn@latest add calendar komutunu yeniden çalıştırır, mevcut dosyanın üzerine yazılsın mı sorusuna Yes yanıtı verirsiniz. Komut hem bileşeni hem react-day-picker paketini günceller. Tailwind v3 kullanıyorsanız belgede verilen v3 uyumlu kodu elle calendar.tsx dosyasına kopyalarsınız; bu kod CSS değişkenlerini h-[--cell-size] gibi v3 sözdizimiyle yazar.
Her iki yolda da belge aynı uyarıyı kalın harflerle yapar: bileşende kendi değişiklikleriniz varsa, onları yeni sürümle elle birleştirmeniz gerekir. Ardından mevcut kullanımlarınızı DayPicker'ın kendi yükseltme rehberine göre güncellersiniz. Yükseltmeden sonra yeni bloklar npx shadcn@latest add calendar-02 gibi komutlarla eklenir. Tailwind sürüm geçişinin shadcn tarafı Tailwind v4 geçişi yazısında ele alınıyor.
npx shadcn@latest add calendar-02Bileşen sayfasındaki değişiklik kaydı 2025-10-26 tarihli bir düzeltmeyi de anlatır: showWeekNumber açıkken seçili ilk günün sol köşe yuvarlaması uygulanmıyordu, çünkü hafta numarası sütunu yüzünden ilk gün aslında ikinci çocuk öğeydi. Düzeltme, classNames içindeki day sınıfında hafta numarası açıksa nth-child(2), kapalıysa first-child seçicisini kullanır. Eski bir kopyanız varsa bu satırı elle güncellemeniz gerekir.
Sık yapılan hatalar
- Ay indeksini bir fazla yazmak:
new Date(2025, 6, 15)Temmuz'dur, Haziran değil. Örnek veriyi uyarlarken ayları sıfırdan saymayı unutmayın. - Aralık modunda tek tarih tipi kullanmak:
mode="range"ile durum tipiDateRange | undefinedolmalıdır;Datetipi verirseniz TypeScript sizi uyarır ve değerinfrom/toalanlarına ulaşamazsınız. - Saat dilimini render sırasında okumak: sunucu tarafında oluşturulan sayfalarda hydration uyuşmazlığına yol açar. Belgedeki gibi
useEffectiçinde okuyun. - Popover genişliğini sabit bırakmak:
PopoverContentvarsayılan olarakw-72genişliğinde vep-4boşluklu gelir. Takvim için örneklerdeki gibiw-auto p-0verin. - Güncellemede değişiklikleri kaybetmek:
add calendarile üzerine yazmayı onaylamadan önce kendi düzenlemelerinizi bir yere not edin, çünkü yeni dosya onları içermez. - Button bileşenini atlamak: elle kurulumda
Buttonyoksacalendar.tsxiçindeki import çözülemez.
Sık Sorulan Sorular
Date Picker için ayrı bir komut var mı?
Hayır. Belge Date Picker'ı Popover ve Calendar bileşenlerinin birleşimi olarak tanımlar; bu iki bileşeni ekleyip örnek kodu kendi dosyanıza yazarsınız. Hazır bir sürüm istiyorsanız takvim bloklarından birini, örneğin doğum tarihi seçiciyi blok adıyla ekleyebilirsiniz.
Takvimde saat seçimi yapılabiliyor mu?
Belgedeki saatli örnek, saati takvimin içinde değil, yanında duran type="time" özellikli bir Input ile alır. Takvim yalnızca günü seçer.
Seçtiğim gün neden bir gün önce görünüyor?
Belge bu belirtiyi saat dilimi farkına bağlar ve timeZone prop'una kullanıcının yerel saat dilimini vermenizi önerir. Değeri Intl.DateTimeFormat().resolvedOptions().timeZone ile, efekt içinde alın.
Takvim bloklarını nasıl eklerim?
Blok adını add komutuna verirsiniz: npx shadcn@latest add calendar-02. Blokların genel kullanımı shadcn/ui blocks yazısında anlatılıyor.
Seçili günün rengini nasıl değiştiririm?
Gün düğmesi tek seçimde primary, aralık ortasında accent rengini kullanır. Tema değişkenlerini değiştirebilir ya da CalendarDayButton içindeki sınıfları kendi dosyanızda düzenleyebilirsiniz; dosya sizin olduğu için ikisi de geçerli yoldur.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: shadcn/ui öğrenme rehberi · Popover, Tooltip ve Hover Card · Select ve Native Select · React Hook Form ve Zod ile doğrulama · shadcn/ui blocks · Tailwind v4 geçişi