Rxova
Skip to content

Date input — Migrating

Every migration here trades a calendar for a typed field, so the honest first question is whether your users are choosing a date or stating one. Birthdays, invoice dates, expiry dates and passport numbers are stated. Meetings and flights are chosen. Keep the calendar for the second kind, or offer both.

react-datepicker@rxova/react-date-inputNotes
selected (Date)value (string | null)'2026-03-15', never a Date
onChange(date)onChange(iso)Fires only for a complete, real date
minDate / maxDate (Date)min / max (string)Inclusive, ISO
dateFormatlocaleOrder comes from the locale, not a format string
locale (a date-fns locale object)locale (a BCP 47 tag)No import, no registration
placeholderTextplaceholdersPer segment
disabled / readOnlydisabled / readOnlySame
namenameEmits a hidden input with the ISO value
showTimeSelect, selectsRange, inline, …Out of scope; keep a calendar for these
import { useState } from 'react'
import DatePicker from 'react-datepicker'
import 'react-datepicker/dist/react-datepicker.css'
function Form() {
const [date, setDate] = useState<Date | null>(null)
return <DatePicker selected={date} onChange={setDate} dateFormat="dd/MM/yyyy" />
}
import { useState } from 'react'
import { DateInput } from '@rxova/react-date-input'
function Form() {
const [date, setDate] = useState<string | null>(null)
return <DateInput label="Date" locale="en-GB" value={date} onChange={setDate} />
}

You drop date-fns, @floating-ui/react, clsx and the stylesheet import.

If the rest of your app still wants Date objects, convert where the timezone question can actually be answered — not inside the component:

import { fromISO, toISO } from '@rxova/react-date-input'
/** Local midnight. Use this when the date means "a day in the user's calendar". */
function toLocalDate(iso: string): Date {
const parts = fromISO(iso)
if (!parts) throw new Error(`Not an ISO date: ${iso}`)
return new Date(parts.year, parts.month - 1, parts.day)
}
/** The inverse, reading back the same local fields. */
function fromLocalDate(date: Date): string | null {
return toISO({ year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() })
}

Note both functions use the local accessors consistently. Mixing new Date(iso) (UTC) with .getDate() (local) is the bug this package exists to avoid.

react-day-picker is a calendar and nothing else — there is no text input to replace. The migration is a product decision: replace the popover-and-grid with a typed field where the date is known.

import { useState } from 'react'
import { DateInput } from '@rxova/react-date-input'
function Birthday() {
const [value, setValue] = useState<string | null>(null)
return <DateInput label="Date of birth" value={value} onChange={setValue} max="2026-07-29" />
}

selected/onSelect become value/onChange, disabled={{ before, after }} becomes min/max, and date-fns leaves the bundle.

The closest relative: react-aria’s DateField is also a segmented, keyboard-first field, and the interaction model carries over almost exactly. The differences are the dependency and the value type.

react-aria@rxova/react-date-inputNotes
CalendarDate from @internationalized/datestring'2026-03-15'
useDateFieldState + useDateSegmentuseDateInputOne hook, one package
granularityDate only
isDisabled / isReadOnly / isInvaliddisabled / readOnly / invalidRenamed
minValue / maxValuemin / maxISO strings
import { useState } from 'react'
import { DateInput } from '@rxova/react-date-input'
function Field() {
const [value, setValue] = useState<string | null>('2026-03-15')
return <DateInput label="Date" value={value} onChange={setValue} />
}

You lose @internationalized/date’s calendar systems and time support. You gain a single zero-dependency package and a value you can put straight into JSON.

The native control is genuinely good and free, and you should keep it unless one of these applies: its appearance cannot be styled consistently across engines, its calendar cannot be removed, and on some platforms it cannot be operated in the format your users expect.

The value format is identicalYYYY-MM-DD — so the swap is mechanical:

import { DateInput } from '@rxova/react-date-input'
// Before
function Before() {
return <input type="date" name="due" min="2026-01-01" />
}
// After
function After() {
return <DateInput label="Due" name="due" min="2026-01-01" />
}

onChange gives you the string directly rather than event.target.value.