reapex-date
v0.2.0
Published
Headless, accessible, fully customizable React Date & Time Picker with i18n, range selection, and data-attribute styling
Maintainers
Readme
ReApexDate
A headless, accessible, and highly customizable React Date & Time Picker for modern applications.
Live Playground · npm · GitHub
Why ReApexDate?
Most datepickers force their design on your app. ReApexDate doesn't.
It handles the hard parts — you control the look and feel.
ReApexDate handles:
✅ Date calculations & calendar math
✅ Single / Range / Multi-select logic
✅ Date + Time with seconds precision
✅ Keyboard navigation (arrow keys, Enter, Escape)
✅ WAI-ARIA accessibility & focus trapping
✅ 8 built-in locales with auto-RTL
✅ Min/max dates, disabled dates
✅ Typed input with auto-inserted separators (new in 0.2)
✅ Dual-month view, portal, match-width popover (new in 0.2)
✅ Form props, error state, uncontrolled mode (new in 0.2)
YOU control:
🎨 Design & colors
🎨 CSS framework (Tailwind, vanilla, anything)
🎨 Layout & spacing
🎨 Typography & branding
🎨 Component structure
Style everything through data-* attributes — no CSS imports, no style overrides, no !important hacks.
📦 Install
npm install reapex-datePeer deps: react >= 18.0.0, react-dom >= 18.0.0
Installed automatically: dayjs, @floating-ui/react
⚡ 30-Second Quick Start
import { useState } from 'react';
import { DatePickerInput } from 'reapex-date';
function App() {
const [date, setDate] = useState<Date | null>(null);
return (
<DatePickerInput
config={{ mode: 'single', value: date, onChange: setDate }}
placeholder="Pick a date..."
allowClear
/>
);
}That's it. No CSS import. No theme provider. Just works.
🎯 What Can You Build?
Single Date Picker
<DatePickerInput
config={{ mode: 'single', value, onChange: setValue }}
locale={en}
placeholder="Select date..."
/>Date Range Picker
<DatePickerInput
config={{ mode: 'range', value: range, onChange: setRange }}
format="DD/MM/YYYY"
placeholder="Start → End"
/>DateTime Picker (with Input + Popover)
<DateTimePickerInput
value={dateTime}
onChange={setDateTime}
use12Hour // 12h with AM/PM
showSeconds // HH:MM:SS precision
minuteStep={5} // 5-min intervals
okText="Confirm"
/>Multi-Select Calendar
<CalendarRoot
config={{ mode: 'multiple', value: dates, onChange: setDates }}
renderFooter={() => <span>{dates.length} selected</span>}
/>Localized (RTL Arabic)
import { ar } from 'reapex-date';
<DatePickerInput config={config} locale={ar} />
// Automatically sets dir="rtl", Arabic month/day names, Saturday start🆕 What's New in 0.2.0
| Feature | Prop | Notes |
|---------|------|-------|
| Typed input + auto separators | editable | Type 15012026 → 15/01/2026. Committed on Enter / blur, invalid or disabled dates are rejected (onInvalidInput). Single & range modes. Opt-in (default false). |
| Dual-month view | monthsToShow={2} | Two months side by side, works in every mode. Default 1. |
| Portal | portal, portalContainer | Render the popover in document.body so overflow:hidden parents can't clip it. |
| Match input width | matchWidth | Popover is at least as wide as the input. |
| Placement | placement | Optional override. Default is automatic: flips upward / aligns to the end when there is no room. |
| Form integration | name, id, required, ref, onFocus, onBlur | Renders hidden input(s) for native form submit (display format; range → name_start / name_end). ref points at the visible input. |
| Error state | error | Sets aria-invalid + data-invalid on the wrapper. Style it yourself, e.g. [data-datepicker-wrapper][data-invalid] [data-datepicker-input] { border-color: #ef4444 } |
| Uncontrolled mode | config.defaultValue | Omit value and let the picker manage its own state; onChange still fires. |
| Smarter fallback placement | — | Popover flip fallbacks are now top-start, top-end, bottom-end. |
// Typed range with a native form field
<form>
<DatePickerInput
editable
name="period"
format="DD/MM/YYYY"
monthsToShow={2}
portal
matchWidth
required
error={hasError}
config={{ mode: 'range', value: range, onChange: setRange }}
onInvalidInput={(text) => console.warn('Bad date:', text)}
/>
</form>
// Uncontrolled
<DatePickerInput config={{ mode: 'single', defaultValue: new Date(), onChange: console.log }} />Use shouldDisableDate: (date) => boolean in config to block custom dates (e.g. weekends); it also applies to typed input.
New utilities: applyDateMask, getMaskLength, isMaskableFormat. CalendarRoot gained monthsToShow and style; useDatePickerPopover accepts { placement, matchWidth }.
🌍 Built-in Locales
| Locale | Code | Direction | Week Start |
|--------|------|-----------|------------|
| English | en | LTR | Sunday |
| Hindi | hi | LTR | Sunday |
| French | fr | LTR | Monday |
| German | de | LTR | Monday |
| Spanish | es | LTR | Monday |
| Japanese | ja | LTR | Sunday |
| Chinese | zh | LTR | Monday |
| Arabic | ar | RTL | Saturday |
🎨 Styling with Data Attributes
ReApexDate renders unstyled semantic HTML. Style it with any CSS framework:
/* Selected date */
[data-calendar-grid] td button[data-selected="true"] {
background: #2563eb;
color: white;
border-radius: 50%;
}
/* Today */
[data-calendar-grid] td button[data-today="true"] {
outline: 2px solid #3b82f6;
font-weight: bold;
}
/* Range highlight */
[data-calendar-grid] td button[data-in-range="true"] {
background: #dbeafe;
}
/* Disabled */
[data-calendar-grid] td button[data-disabled="true"] {
opacity: 0.3;
cursor: not-allowed;
}All Data Attributes
| Attribute | Element | Description |
|-----------|---------|-------------|
| data-selected | Day button | Currently selected |
| data-today | Day button | Today's date |
| data-in-range | Day button | Between range start/end |
| data-range-start | Day button | First date of range |
| data-range-end | Day button | Last date of range |
| data-outside-month | Day button | Prev/next month dates |
| data-disabled | Day button | Cannot be selected |
| data-focused | Day button | Keyboard focus |
| data-time-cell | Time button | Hour/minute/second cells |
| data-calendar-root | Container | Root element |
| data-view | Container | day / month / year |
🏗️ Architecture
src/
├── engine/ # Pure date math (zero React imports)
├── hooks/ # Stateful React layer
├── a11y/ # Keyboard nav, focus trap, click-outside
├── locales/ # 8 i18n configurations
├── components/
│ ├── Calendar/ # CalendarRoot, Header, Grid, MonthView, YearView
│ ├── DatePickerInput/ # Input + popover for dates
│ ├── TimePicker/ # Scrollable hour/min/sec columns
│ ├── DateTimePicker/ # Inline calendar + time
│ └── DateTimePickerInput/ # Input + popover for date+time
└── utils/ # formatDate, dateAdapterEngine layer is framework-agnostic — pure functions with zero React imports. Can be ported to Vue, Svelte, or vanilla JS.
📊 Package Size
| Metric | Size |
|--------|------|
| npm tarball (npm pack) | ~52 KB |
| Unpacked | ~244 KB (JS + type declarations) |
| Runtime dependencies | dayjs, @floating-ui/react (installed once, not bundled) |
Tree-shakeable (sideEffects: false). Only pay for what you import.
🤝 Contributing
Contributions are welcome! Feel free to open issues or submit pull requests.
📄 License
MIT — Use it freely in personal and commercial projects.
