NewReUI Pro is now available! Get 20% off with early bird pricing.View pricing
Overview
  • Introduction
  • Get Started
  • License Setup
  • Styling
  • MCP
  • Registry
  • Roadmap
  • Changelog
  • llms.txt
  • v1 Docs
Components
  • Alert
  • Autocomplete
  • Badge
  • Data GridVirtualization and row pinning support added
  • Date Selector
  • File Upload
  • Filters
  • Frame
  • Shadcn Icon Stack
  • Kanban
  • Number Field
  • Phone Input
  • Rating
  • Scrollspy
  • Sortable
  • Stepper
  • Timeline
  • Tree

Application

  • Authentication
  • Card
  • Chart
  • Data Grid
  • Dialog
  • Browse all

eCommerce

  • Shopping Cart
  • Category Card
  • Checkout
  • Comparison
  • Coupon
  • Browse all

Marketing

  • Blog
  • Comparison Table
  • Contact
  • Content Section
  • CTA
  • Browse all

SaaS

  • Analytics
  • Billing
  • Dashboard
  • Integrations
  • Notifications
  • Browse all

Fintech

  • Accounts
  • Transactions
  • Transfer
  • Cards
  • Investments
  • Browse all

Dev Tools

  • API Console
  • CI/CD
  • Code Editor
  • Debug Panel
  • Documentation
  • Browse all

AI & LLM

  • AI Playground
  • AI Settings
  • Chat Interface
  • Embeddings
  • Evaluation
  • Browse all

Data Visualization

  • Charts
  • Dashboards
  • Heatmaps
  • Maps
  • Metrics
  • Browse all

Resources

  • Components
  • Blocks
  • Docs
  • Help & Contact
  • Pricing
  • RoadmapSoon
  • AffiliateSoon

Legal

  • Privacy Policy
  • Terms & Conditions
  • Licensing
  • Cookies

© 2026 ReUI. All rights reserved.

ComponentsBlocksIconsTemplatesDocsPricing
X
LoginGet All-access
2.5k

Shadcn Date Selector

PreviousNext

Custom Shadcn Date Selector for React and Tailwind CSS. A universal date selector component with multiple period types, filter modes, and flexible display options.

Base UIRadix UI
Radix UI

Installation

pnpm dlx shadcn@latest add @reui/r-date-selector

Usage

More Shadcn Date Selector Components

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.

Data GridFile Upload

On This Page

InstallationUsageExamplesWith PopoverWith DialogWith i18n SupportAPI ReferenceDateSelectorInterfacesDateSelectorValue
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" />

Examples

With Popover

"use client"

import { useEffect, useState } from "react"
import {
  DateSelector,
  formatDateValue,
  type DateSelectorValue,
} from "@/components/reui/date-selector"

import { Button } from "@/components/ui/button"
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover"
import { Separator } from "@/components/ui/separator"
import { CalendarIcon } from 'lucide-react'

export function Pattern() {
  const [value, setValue] = useState<DateSelectorValue | undefined>()
  const [open, setOpen] = useState(false)
  const [internalValue, setInternalValue] = useState<
    DateSelectorValue | undefined
  >(value)

  const formattedValue = value ? formatDateValue(value) : ""
  const displayText = formattedValue || "Select a date"

  useEffect(() => {
    if (open) {
      setInternalValue(value)
    }
  }, [open, value])

  const handleApply = () => {
    setValue(internalValue)
    setOpen(false)
  }

  const handleCancel = () => {
    setInternalValue(value)
    setOpen(false)
  }

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <Button variant="outline" className="w-56 justify-start">
          <CalendarIcon />
          {displayText}
        </Button>
      </PopoverTrigger>
      <PopoverContent className="w-auto gap-3 p-0" align="start" sideOffset={4}>
        <div className="p-3">
          <DateSelector
            value={internalValue}
            onChange={setInternalValue}
            allowRange={true}
            label="Due date"
            inputHint="Try: 2025, Q4, 05/10/2025"
          />
        </div>
        <Separator className="p-0" />
        <div className="flex justify-end gap-2 p-3 pt-0">
          <Button variant="outline" onClick={handleCancel}>
            Cancel
          </Button>
          <Button onClick={handleApply}>Apply</Button>
        </div>
      </PopoverContent>
    </Popover>
  )
}

With Dialog

"use client"

import { useEffect, useState } from "react"
import {
  DateSelector,
  formatDateValue,
  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 { CalendarIcon } from 'lucide-react'

export function Pattern() {
  const [value, setValue] = useState<DateSelectorValue | undefined>()
  const [open, setOpen] = useState(false)
  const [internalValue, setInternalValue] = useState<
    DateSelectorValue | undefined
  >(value)

  const formattedValue = value ? formatDateValue(value) : ""
  const displayText = formattedValue || "Select a date"

  useEffect(() => {
    if (open) {
      setInternalValue(value)
    }
  }, [open, value])

  const handleApply = () => {
    if (internalValue) {
      setValue(internalValue)
    }
    setOpen(false)
  }

  return (
    <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>Select Due Date</DialogTitle>
        </DialogHeader>

        <DateSelector
          value={internalValue}
          onChange={setInternalValue}
          showInput={true}
        />

        <DialogFooter>
          <DialogClose asChild>
            <Button variant="outline">Cancel</Button>
          </DialogClose>
          <Button onClick={handleApply}>Apply</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

With i18n Support

API Reference

DateSelector

The main component for selecting dates and time periods.

PropTypeDefaultDescription
valueDateSelectorValue-The current value of the selector.
onChange(value: DateSelectorValue) => void-Callback fired when the value changes.
allowRangebooleantrueWhether to allow selecting date ranges (using "between" operator).
periodTypesDateSelectorPeriodType[]-List of available period types (day, month, etc.).
defaultPeriodTypeDateSelectorPeriodType"day"The initial period type.
defaultFilterTypeDateSelectorFilterType"is"The initial filter operator.
presetModeDateSelectorFilterType-If set, locks the selector to this specific filter mode.
showInputbooleantrueWhether to show the text input field above the selector.
showTwoMonthsbooleantrueWhether to show two months in the calendar view (desktop only).
labelstring-Optional label to display above the filter toggle.
yearRangenumber10The number of years to show in the year/month/quarter list.
baseYearnumbernew Date().getFullYear()The reference year for the year selection list.
minYearnumber2015The minimum selectable year.
maxYearnumber2026The maximum selectable year.
i18nPartial<DateSelectorI18nConfig>-Custom labels and operators for internationalization.
inputHintstring-Optional hint text to display in the input field when empty and focused.
dayDateFormatstring"MM/dd/yyyy"The format used to display single days.
classNamestring-Additional CSS classes for the container.

Interfaces

DateSelectorValue

The structure of the value returned by the onChange callback.

PropertyTypeDescription
periodDateSelectorPeriodTypeThe selected period type (day, month, quarter, half-year, year).
operatorDateSelectorFilterTypeThe selected operator (is, before, after, between).
startDateDateThe selected start date (for day period).
endDateDateThe selected end date (for day period range).
yearnumberThe selected year.
monthnumberThe selected month (0-11).
quarternumberThe selected quarter (0-3).
halfYearnumberThe selected half-year (0-1).
rangeStart{ year: number; value: number }The start of a period range.
rangeEnd{ year: number; value: number }The end of a period range.
"use client"

import { useState } from "react"
import {
  DateSelector,
  type DateSelectorValue,
} from "@/components/reui/date-selector"
import { format } from "date-fns"

import { Card, CardContent } from "@/components/ui/card"

export function Pattern() {
  const [value, setValue] = useState<DateSelectorValue | undefined>()

  return (
    <div className="flex w-full flex-col items-center gap-5">
      <Card className="p-0">
        <CardContent className="p-3">
          <DateSelector
            value={value}
            onChange={setValue}
            label="Due date"
            inputHint="Try: 2025, Q4, 05/10/2025"
          />
        </CardContent>
      </Card>

      {value ? (
        <pre className="bg-muted w-full overflow-auto rounded-md p-3 font-mono text-xs md:w-[500px]">
          {JSON.stringify(
            value,
            (key, val) => {
              if (val instanceof Date) {
                return format(val, "MM/dd/yyyy")
              }
              return val
            },
            2
          )}
        </pre>
      ) : (
        <div className="text-muted-foreground text-sm">
          No value selected. Select a date to see the debug information.
        </div>
      )}
    </div>
  )
}
"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>
  )
}