@sandylib27/react-aria-timepicker
v0.1.0
Published
A Tailwind-first React time picker built on react-aria-components. Zero config, classNames customization, optional slots.
Maintainers
Readme
@sandylib27/react-aria-timepicker
A Tailwind-first React time picker built on react-aria-components. Zero config, accessible, customizable.
Why?
- No extra CSS required — works out of the box with Tailwind CSS
- Built on react-aria — full accessibility (ARIA patterns, keyboard nav, screen readers)
- No design system lock-in — no Radix, no shadcn, no Material UI dependency
- classNames prop — override any visual part without forking
- Optional slots — swap in your own Button/Input/Icon components
- Tree-shakeable — only ship what you use
Installation
npm install @sandylib27/react-aria-timepicker react-aria-components @internationalized/dateQuick Start
import { TimePicker } from '@sandylib27/react-aria-timepicker'
import { useState } from 'react'
function App() {
const [time, setTime] = useState('')
return <TimePicker value={time} onChange={setTime} />
}That's it. If you have Tailwind CSS configured, it looks good immediately.
TimePicker Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| value | string | — | Time as HH:mm or HH:mm:ss |
| onChange | (value: string) => void | — | Called when time changes |
| placeholder | string | — | Placeholder text for input |
| showHour | boolean | true | Show hours column |
| showMinute | boolean | true | Show minutes column |
| showSecond | boolean | true | Show seconds column |
| hourStep | number | 1 | Hour increment step |
| minuteStep | number | 1 | Minute increment step |
| secondStep | number | 1 | Second increment step |
| disabledHours | () => number[] | — | Return hours to disable |
| disabledMinutes | (hour) => number[] | — | Return minutes to disable |
| disabledSeconds | (hour, minute) => number[] | — | Return seconds to disable |
| showClear | boolean | false | Show clear button in panel |
| placement | Placement | "bottom start" | Panel popover position |
| isDisabled | boolean | false | Disable the picker |
| hasError | boolean | false | Show error border state |
| isEdited | boolean | false | Show edited border state |
| hideFocusRing | boolean | false | Hide focus ring when open |
| classNames | TimePickerClassNames | — | Override classes for any part |
| slots | TimePickerSlots | — | Swap sub-components |
| className | string | — | Root container class |
Customizing with classNames
Override any part of the component:
<TimePicker
value={time}
onChange={setTime}
classNames={{
panel: "bg-white dark:bg-zinc-900 shadow-xl rounded-xl border-0",
columnItemSelected: "bg-indigo-600 text-white",
columnItemHover: "hover:bg-indigo-100 dark:hover:bg-indigo-900",
inputWrapper: "border-gray-300 rounded-md",
}}
/>Available classNames keys
root, inputWrapper, input, inputIcon, panel, panelInputWrap, segmentWrap, segmentInput, segmentSeparator, column, columnDivider, columnItem, columnItemSelected, columnItemFocused, columnItemHover, clearWrapper, clearButton
Swapping Components via Slots
For projects using shadcn/ui or other design systems, swap internal primitives:
import { TimePicker } from '@sandylib27/react-aria-timepicker'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
function MyTimePicker() {
return (
<TimePicker
value={time}
onChange={setTime}
slots={{
Button: ({ children, className, onClick }) => (
<Button variant="outline" className={className} onClick={onClick}>
{children}
</Button>
),
Input: Input,
Icon: ({ name, size, className }) => (
<MyIcon name={name} size={size} className={className} />
),
}}
/>
)
}Slot interfaces
interface TimePickerSlots {
Button?: React.ComponentType<{ onClick?; className?; children; disabled? }>
Input?: React.ComponentType<React.InputHTMLAttributes<HTMLInputElement>>
Icon?: React.ComponentType<{ name; size?; strokeWidth?; className? }>
}Non-Tailwind Projects
If you're not using Tailwind, import the optional CSS file for baseline styles:
import '@sandylib27/react-aria-timepicker/css'
import { TimePicker } from '@sandylib27/react-aria-timepicker'Override with CSS variables:
[data-timepicker],
[data-time-picker-panel] {
--tp-primary: #8b5cf6;
--tp-bg: #1e1b4b;
}Features
- Segment editing — individually editable HH:MM:SS segments with keyboard input
- Scroll columns — scrollable hour/minute/second lists built on react-aria ListBox
- Arrow key navigation — increment/decrement values with arrow keys
- Step intervals — configure
hourStep,minuteStep,secondStep(e.g., 15-minute intervals) - Disabled values — disable specific hours, minutes, or seconds via callbacks
- Portal rendering — panel renders in a portal to escape overflow containers
- Dark mode — built-in dark mode support via Tailwind
dark:prefix
Tech Stack
- react-aria-components — ListBox, accessibility
- @internationalized/date — Immutable Time type
- Tailwind CSS — Default styling (optional)
- clsx + tailwind-merge — Class merging
License
MIT
