react ~13 KB 0 deps v0.3.3 ↗ GitHub ↗

@mshafiqyajid/react-date-picker

Headless date picker hook and styled component. Single and range mode, multi-month view, captionLayout dropdowns, renderDay slot, modifiers, inline calendar, full floating-UI placement (flip + shift), live-region month announcements, keyboard navigation that skips disabled cells. Zero dependencies.

Playground #

Props
TSX
import { DatePickerStyled } from "@mshafiqyajid/react-date-picker/styled";
import "@mshafiqyajid/react-date-picker/styles.css";

<DatePickerStyled
  value={value}
  onChange={setValue}
/>

Install #

npm install @mshafiqyajid/react-date-picker

Quick start #

import { DatePickerStyled } from "@mshafiqyajid/react-date-picker/styled";
import "@mshafiqyajid/react-date-picker/styles.css";

const [date, setDate] = useState<Date | null>(null);
<DatePickerStyled value={date} onChange={setDate} tone="primary" />

// Range mode
const [range, setRange] = useState<[Date, Date] | null>(null);
<DatePickerStyled value={range} onChange={setRange} mode="range" />

Headless #

import { useDatePicker } from "@mshafiqyajid/react-date-picker";

const {
  days,
  month,
  year,
  getDayProps,
  prevMonth,
  nextMonth,
} = useDatePicker({
  mode: "single",
  defaultValue: new Date(),
});

return (
  <div className="cal">
    <header>
      <button onClick={prevMonth}>‹</button>
      <span>{month + 1} / {year}</span>
      <button onClick={nextMonth}>›</button>
    </header>
    <div className="grid">
      {days.map((day) => (
        <button key={day.toISOString()} {...getDayProps(day)}>
          {day.getDate()}
        </button>
      ))}
    </div>
  </div>
);

API #

PropTypeDefaultDescription
value / defaultValueDate | [Date, Date] | nullControlled or uncontrolled value
onChange(v) => voidCalled on selection
mode"single" | "range""single"Single date or date range
minDate / maxDateDateDate bounds
disabledDatesDate[] | (date) => booleanDates to disable
placeholder / formatstring"Select date" / "MMM D, YYYY"Trigger text
size / tone"sm" | "md" | "lg" / "neutral" | "primary""md" / "neutral"Visual
disabled / clearablebooleanfalseExisting behavior
weekStartsOn / locale0–6 / string0 / "en-US"Localization

0.2.0 additions #

PropTypeDefaultDescription
numberOfMonths1 | 2 | 31Render N month grids side-by-side in the popover
pagedBy1 | "all"1Chevron step. "all" advances by numberOfMonths.
renderDay(ctx) => ReactNodeReplace day cell content. ctx: { date, isToday, isSelected, isInRange, isDisabled, isOutsideMonth, modifiers }
captionLayout"label" | "dropdown" | "dropdown-months" | "dropdown-years""label"Header style — chevron-only label or native dropdowns
fromYear / toYearnumbernow-100 / now+10Year dropdown bounds
inlinebooleanfalseRender the calendar always-visible without a trigger
open / defaultOpen / onOpenChangeboolean / fnControlled-open API for the popover
month / defaultMonth{ month, year }Controlled / initial visible month
onMonthChange / onYearChange(...) => voidNavigation callbacks
modifiersRecord<string, Date[] | (date) => boolean>Tag dates; emits data-mod-<name>="true" + data-modifiers="..." per cell
showOutsideDaysbooleantrueRender leading/trailing outside-month cells
fixedWeeksbooleantruePad to 6 rows even for short months
skipDisabledOnArrowKeybooleantrueArrow keys skip disabled cells
closeOnSelectbooleansingle: true, range: falseAuto-close after selection
clearOnReselectbooleanfalseClicking the selected day again clears it
monthChangeAnnouncement(month, year) => string"${month} ${year}"Live-region announcement copy
onDayMouseEnter / onDayMouseLeave / onDayFocus(date, e?) => voidDay-level hooks for tooltips, etc.
placementbottom-start | bottom-end | top-start | top-end"bottom-start"Popover placement
offset / collisionPaddingnumber6 / 8Floating-UI distances
flip / shiftbooleantrueAuto-flip + shift back into view
strategy"absolute" | "fixed""absolute"CSS positioning strategy
Edit this page on GitHub