Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@sandylib27/react-aria-datepicker

A Tailwind-first React date picker and date range 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/Select/Input/Icon components
  • Tree-shakeable — only ship what you use

Installation

npm install @sandylib27/react-aria-datepicker react-aria-components @internationalized/date

Quick Start

import { DatePicker } from '@sandylib27/react-aria-datepicker'
import { CalendarDate } from '@internationalized/date'
import { useState } from 'react'

function App() {
  const [date, setDate] = useState<CalendarDate | null>(null)

  return <DatePicker value={date} onChange={setDate} />
}

That's it. If you have Tailwind CSS configured, it looks good immediately.

DatePicker Props

Prop Type Default Description
value CalendarDate | null Selected date
onChange (date: CalendarDate | null) => void Called when date changes
dateFormat string "dd-mm-yyyy" Format pattern for text input
minValue CalendarDate Minimum selectable date
maxValue CalendarDate Maximum selectable date
placement Placement "bottom start" Popover position
showClear boolean false Show clear button in calendar
hideMonthDropdown boolean false Show plain text instead of dropdown
hideYearDropdown boolean false Show plain text instead of dropdown
isDisabled boolean false Disable the picker
hasError boolean false Show error border state
isEdited boolean false Show edited border state
readOnly boolean false Make input read-only
classNames DatePickerClassNames Override classes for any part
slots DatePickerSlots Swap sub-components
className string Root container class

DateRangePicker Props

Same as DatePicker plus:

Prop Type Default Description
value DateRangeValue | null { start, end }
onChange (range: DateRangeValue | null) => void Called on confirm
errorMessage string Error text below inputs
renderTrigger (props) => ReactNode Custom trigger render

Customizing with classNames

Override any part of the component:

<DatePicker
  value={date}
  onChange={setDate}
  classNames={{
    calendar: "bg-white dark:bg-zinc-900 shadow-xl rounded-xl border-0",
    daySelected: "bg-indigo-600 text-white",
    dayHover: "hover:bg-indigo-100 dark:hover:bg-indigo-900",
    input: "border-gray-300 rounded-md",
    header: "border-b-0 pb-2",
    navButton: "text-indigo-600 hover:bg-indigo-50",
  }}
/>

Available classNames keys

root, input, inputWrapper, inputIcon, calendar, header, navButton, monthSelect, yearSelect, selectContainer, heading, grid, weekday, day, daySelected, dayHover, dayOutside, dayDisabled, dayToday, dayRangeMiddle, dayRangeStart, dayRangeEnd, footer, clearButton, confirmButton, cancelButton

Swapping Components via Slots

For projects using shadcn/ui or other design systems, swap internal primitives:

import { DatePicker } from '@sandylib27/react-aria-datepicker'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'

function MyDatePicker() {
  return (
    <DatePicker
      value={date}
      onChange={setDate}
      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 DatePickerSlots {
  Button?: React.ComponentType<{ onClick?; className?; children; disabled? }>
  Select?: React.ComponentType<{ value; onValueChange; options; className? }>
  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-datepicker/css'
import { DatePicker } from '@sandylib27/react-aria-datepicker'

Override with CSS variables:

[data-datepicker],
[data-datepicker-calendar] {
  --dp-primary: #8b5cf6;
  --dp-bg: #1e1b4b;
}

DateRangePicker

import { DateRangePicker } from '@sandylib27/react-aria-datepicker'
import type { DateRangeValue } from '@sandylib27/react-aria-datepicker'

function App() {
  const [range, setRange] = useState<DateRangeValue | null>(null)

  return <DateRangePicker value={range} onChange={setRange} />
}

Features:

  • Dual-month calendar view
  • Draft/confirm pattern (changes only apply on "Confirm")
  • Built-in start-before-end validation
  • Custom trigger via renderTrigger prop

Migrating from react-day-picker

  1. Install: npm install @sandylib27/react-aria-datepicker react-aria-components @internationalized/date
  2. Uninstall: npm uninstall react-day-picker
  3. Delete any datePickerBase.css or react-day-picker CSS imports
  4. Replace your component:
// Before (react-day-picker)
import { DayPicker } from 'react-day-picker'
const [date, setDate] = useState<Date | null>(null)

// After (react-aria-datepicker)
import { DatePicker, CalendarDate, today, getLocalTimeZone } from '@sandylib27/react-aria-datepicker'
const [date, setDate] = useState<CalendarDate | null>(null)

Key difference: this library uses CalendarDate (from @internationalized/date) instead of native Date. This gives you timezone-safe, immutable date values.

Tech Stack

License

MIT

About

A Tailwind-first React date picker and date range picker built on react-aria-components. Zero config, classNames customization, optional slots.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages