Custom Shadcn Date Selector for React and Tailwind CSS. A universal date selector component with multiple period types, filter modes, and flexible display options.
Browse 4 production-ready Shadcn Date Selector components for dashboards, forms, and product UI. These examples follow the Radix UI implementation with accessible primitives from the Radix stack and stay fully compatible with Shadcn Create so radius, color, and typography match your configured theme.
Browse all 4 Shadcn Date Selector components for copy-ready layouts, dashboards, and forms built with Tailwind CSS in the ReUI library.
import {
DateSelector,
type DateSelectorValue,
} from "@/components/reui/r-date-selector"const [value, setValue] = useState<DateSelectorValue | undefined>()
return <DateSelector value={value} onChange={setValue} label="Due date" />The main component for selecting dates and time periods.
The structure of the value returned by the onChange callback.
"use client"
import { useEffect, useMemo, useState } from "react"
import {
DateSelector,
DEFAULT_DATE_SELECTOR_I18N,
formatDateValue,
type DateSelectorI18nConfig,
type DateSelectorValue,
} from "@/components/reui/date-selector"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { CalendarIcon, ChevronDownIcon } from 'lucide-react'
// Helper function to create i18n config from translations
function createI18nConfig(
translations: Partial<DateSelectorI18nConfig>
): DateSelectorI18nConfig {
return { ...DEFAULT_DATE_SELECTOR_I18N, ...translations }
}
// Language-specific translations
const translations: Record<string, Partial<DateSelectorI18nConfig>> = {
es: {
selectDate: "Seleccionar fecha",
apply: "Aplicar",
cancel: "Cancelar",
clear: "Limpiar",
today: "Hoy",
filterTypes: {
is: "es",
before: "antes de",
after: "después de",
between: "entre",
},
periodTypes: {
day: "DÃa",
month: "Mes",
quarter: "Trimestre",
halfYear: "Semestre",
year: "Año",
},
months: [
"Enero",
"Febrero",
"Marzo",
"Abril",
"Mayo",
"Junio",
"Julio",
"Agosto",
"Septiembre",
"Octubre",
"Noviembre",
"Diciembre",
],
monthsShort: [
"Ene",
"Feb",
"Mar",
"Abr",
"May",
"Jun",
"Jul",
"Ago",
"Sep",
"Oct",
"Nov",
"Dic",
],
quarters: ["T1", "T2", "T3", "T4"],
halfYears: ["S1", "S2"],
weekdays: [
"Domingo",
"Lunes",
"Martes",
"Miércoles",
"Jueves",
"Viernes",
"Sábado",
],
weekdaysShort: ["Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sá"],
placeholder: "Seleccionar fecha...",
rangePlaceholder: "Seleccionar rango de fechas...",
},
fr: {
selectDate: "Sélectionner une date",
apply: "Appliquer",
cancel: "Annuler",
clear: "Effacer",
today: "Aujourd'hui",
filterTypes: {
is: "est",
before: "avant",
after: "après",
between: "entre",
},
periodTypes: {
day: "Jour",
month: "Mois",
quarter: "Trimestre",
halfYear: "Semestre",
year: "Année",
},
months: [
"Janvier",
"Février",
"Mars",
"Avril",
"Mai",
"Juin",
"Juillet",
"Août",
"Septembre",
"Octobre",
"Novembre",
"Décembre",
],
monthsShort: [
"Jan",
"Fév",
"Mar",
"Avr",
"Mai",
"Juin",
"Juil",
"Aoû",
"Sep",
"Oct",
"Nov",
"Déc",
],
quarters: ["T1", "T2", "T3", "T4"],
halfYears: ["S1", "S2"],
weekdays: [
"Dimanche",
"Lundi",
"Mardi",
"Mercredi",
"Jeudi",
"Vendredi",
"Samedi",
],
weekdaysShort: ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"],
placeholder: "Sélectionner une date...",
rangePlaceholder: "Sélectionner une plage de dates...",
},
de: {
selectDate: "Datum auswählen",
apply: "Anwenden",
cancel: "Abbrechen",
clear: "Löschen",
today: "Heute",
filterTypes: {
is: "ist",
before: "vor",
after: "nach",
between: "zwischen",
},
periodTypes: {
day: "Tag",
month: "Monat",
quarter: "Quartal",
halfYear: "Halbjahr",
year: "Jahr",
},
months: [
"Januar",
"Februar",
"März",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember",
],
monthsShort: [
"Jan",
"Feb",
"Mär",
"Apr",
"Mai",
"Jun",
"Jul",
"Aug",
"Sep",
"Okt",
"Nov",
"Dez",
],
quarters: ["Q1", "Q2", "Q3", "Q4"],
halfYears: ["H1", "H2"],
weekdays: [
"Sonntag",
"Montag",
"Dienstag",
"Mittwoch",
"Donnerstag",
"Freitag",
"Samstag",
],
weekdaysShort: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
placeholder: "Datum auswählen...",
rangePlaceholder: "Datumsbereich auswählen...",
},
}
// Internationalization configurations
const i18nConfigs: Record<string, DateSelectorI18nConfig> = {
en: DEFAULT_DATE_SELECTOR_I18N,
es: createI18nConfig(translations.es),
fr: createI18nConfig(translations.fr),
de: createI18nConfig(translations.de),
}
// Language metadata
const languageMetadata = {
en: {
label: "English",
flag: "🇺🇸",
dateFormat: "MM/dd/yyyy",
weekStartsOn: 0 as const,
ui: {
label: "Due date",
hint: "Try: 2025, Q4, 05/10/2025",
placeholder: "Select a date",
},
},
es: {
label: "Español",
flag: "🇪🇸",
dateFormat: "dd/MM/yyyy",
weekStartsOn: 1 as const,
ui: {
label: "Fecha de vencimiento",
hint: "Prueba: 2025, T4, 05/10/2025",
placeholder: "Seleccionar una fecha",
},
},
fr: {
label: "Français",
flag: "🇫🇷",
dateFormat: "dd/MM/yyyy",
weekStartsOn: 1 as const,
ui: {
label: "Date d'échéance",
hint: "Essayez: 2025, T4, 05/10/2025",
placeholder: "Sélectionner une date",
},
},
de: {
label: "Deutsch",
flag: "🇩🇪",
dateFormat: "dd.MM.yyyy",
weekStartsOn: 1 as const,
ui: {
label: "Fälligkeitsdatum",
hint: "Versuchen Sie: 2025, Q4, 05.10.2025",
placeholder: "Datum auswählen",
},
},
}
// Language options for the selector
const languageOptions = Object.entries(languageMetadata).map(
([value, meta]) => ({
value,
label: meta.label,
flag: meta.flag,
})
)
export function Pattern() {
const [value, setValue] = useState<DateSelectorValue | undefined>()
const [open, setOpen] = useState(false)
const [internalValue, setInternalValue] = useState<
DateSelectorValue | undefined
>(value)
const [currentLanguage, setCurrentLanguage] =
useState<keyof typeof languageMetadata>("fr")
// Get current language metadata and i18n config
const currentMeta = useMemo(
() => languageMetadata[currentLanguage] || languageMetadata.en,
[currentLanguage]
)
const currentI18n = useMemo(
() => i18nConfigs[currentLanguage] || i18nConfigs.en,
[currentLanguage]
)
useEffect(() => {
if (open) {
setInternalValue(value)
}
}, [open, value])
const formattedValue = useMemo(
() =>
value ? formatDateValue(value, currentI18n, currentMeta.dateFormat) : "",
[value, currentI18n, currentMeta.dateFormat]
)
const displayText = formattedValue || currentMeta.ui.placeholder
const handleApply = () => {
setValue(internalValue)
setOpen(false)
}
const handleCancel = () => {
setInternalValue(value)
setOpen(false)
}
return (
<div className="flex h-full w-full grow flex-col items-stretch gap-4">
<div className="flex w-full justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
className="flex items-center gap-2"
>
<span>{currentMeta.flag}</span>
<span>{currentMeta.label}</span>
<ChevronDownIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
{languageOptions.map((lang) => (
<DropdownMenuItem
key={lang.value}
onClick={() =>
setCurrentLanguage(
lang.value as keyof typeof languageMetadata
)
}
className="flex items-center gap-2"
>
<span>{lang.flag}</span>
<span>{lang.label}</span>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="flex grow items-center justify-center">
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="outline" className="w-56 justify-start">
<CalendarIcon />
{displayText}
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-lg" showCloseButton={false}>
<DialogHeader>
<DialogTitle>{currentMeta.ui.label}</DialogTitle>
</DialogHeader>
<DateSelector
value={internalValue}
onChange={setInternalValue}
showInput={true}
i18n={currentI18n}
dayDateFormat={currentMeta.dateFormat}
weekStartsOn={currentMeta.weekStartsOn}
/>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline" onClick={handleCancel}>
{currentI18n.cancel}
</Button>
</DialogClose>
<Button onClick={handleApply}>{currentI18n.apply}</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
)
}