npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@octax-app/hot-date-react

v0.1.4

Published

React wrapper for the hot-date natural language date input web component.

Readme

hot-date-react

React wrapper for @stolinski/hot-date — a natural language date input web component. Type anything: "next friday", "tomorrow to after tomorrow", "between jan 1 and feb 28", and get a clean ISO date back.

Demo

Live demo →

Install

npm install @octax-app/hot-date-react

Peer dependencies: React ≥ 18

Quick Start

import { HotDate } from '@octax-app/hot-date-react';

function MyForm() {
  return (
    <HotDate
      placeholder="e.g. next friday, tomorrow"
      onChange={(value) => console.log(value)}
    />
  );
}

Usage Examples

Point date (default)

<HotDate
  dateType="point"
  onChange={(value) => console.log(value)}
  // value: "2026-06-13"
/>

Range picker

<HotDate
  dateType="range"
  onChange={(value) => console.log(value)}
  // value: ["2026-06-01", "2026-06-30"]
/>

Combined (point + range)

<HotDate
  dateType="combined"
  onChange={(value) => console.log(value)}
  // "tomorrow"           → "2026-05-18"
  // "this week"          → ["2026-05-12", "2026-05-18"]
  // "jan to feb"         → ["2026-01-01", "2026-02-28"]
/>

combined accepts both single dates and ranges. onChange returns a string for point dates and [string, string] for ranges — the same shapes as the dedicated modes.

Custom output format

<HotDate
  format="MM/DD/YYYY"
  onChange={(value) => console.log(value)}
  // value: "06/13/2026"
/>

Tokens are case-insensitive: YYYY/yyyy, MM/mm, DD/dd, YY/yy, M/m, D/d, MMM (short month name e.g. Jan), MMMM (full month name e.g. January). Month name tokens respect the locale prop.

For ranges, onChange returns [formattedStart, formattedEnd].

When the input loses focus after a value is committed, the field displays the formatted value. When focused again, it restores the raw natural-language input for editing.

Date constraints

Both Date objects and "YYYY-MM-DD" strings are accepted. The output value is always a string.

// Using JS Date objects
<HotDate
  startDate={new Date()}
  endDate={new Date(Date.now() + 30 * 86400000)}
  onChange={(value) => console.log(value)} // value: "2026-06-13" (string)
/>

// Using ISO strings
<HotDate
  startDate="2026-01-01"
  endDate="2026-12-31"
  onChange={(value) => console.log(value)}
/>

Dates outside the startDate/endDate window are rejected at the parser level.

Bring your own CSS

The component renders as a plain browser input by default — no decorative styles are applied. Use className and ::part() to style it, or pass Tailwind classes via classNames.

<HotDate className="my-picker" />
/* Use ::part() to style shadow DOM elements */
.my-picker::part(input) {
  border: 2px solid #6366f1;
  border-radius: 8px;
  padding: 0.5rem 0.75rem;
  font-family: monospace;
}
.my-picker::part(ghost) {
  padding: 0.5rem 0.75rem;
}

Tailwind dark mode

dark: variants work out of the box. The component mirrors all classes from <html> into the shadow root on every render and whenever they change, so Tailwind's .dark .dark\:* selectors resolve correctly inside the shadow DOM.

<HotDate
  classNames={{
    input: "bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 border-gray-300 dark:border-gray-600",
  }}
/>

Toggling document.documentElement.classList.toggle('dark') at runtime is picked up immediately.

Per-part class names (Tailwind-friendly)

Use classNames to apply classes directly to shadow DOM elements. External stylesheets — including Tailwind — are automatically mirrored into the shadow root, so utility classes work out of the box.

Each key accepts a string or a function that receives the current component state as an object:

<HotDate
  classNames={{
    input: ({ active, focused, error }) =>
      `border rounded px-3 py-2 w-full
       ${focused ? "ring-2 ring-indigo-500" : ""}
       ${active ? "border-green-500" : "border-gray-300"}
       ${error ? "border-red-500" : ""}`,
    ghost: "text-gray-400",
    hint: "opacity-50",
  }}
  error={hasError}
/>

The function signature for any classNames entry:

(props: {
  active: boolean;    // true when input has a resolved valid date
  disabled: boolean;  // true when the disabled prop is set
  focused: boolean;   // true when the input currently has focus
  error: boolean;     // true when the error prop is set
  success: boolean;   // true when the success prop is set
}) => string

The keys map to shadow DOM parts:

| Key | Part | Element | | --- | --- | --- | | input | part="input" | The <input> element | | ghost | part="ghost" | The suggestion overlay | | hint | part="hint" | The Tab hint chip |

Controlled value

value accepts the same shapes that onChange returns — so you can pass the value straight back without converting.

// Point date
const [date, setDate] = useState<string>("");

<HotDate
  dateType="point"
  value={date || null}
  onChange={(v) => setDate(v as string)}
/>
// Range — pass the [start, end] array directly back as value
const [range, setRange] = useState<string | [string, string]>("");

<HotDate
  dateType="range"
  value={range || null}
  onChange={setRange}
/>
// After blur shows: "2026-01-01 — 2026-01-31"

When value is provided the input renders in display mode — showing the formatted date or range — while unfocused. Clicking into it restores the natural-language input for editing. On blur it returns to display mode automatically.

onChange never returns null. It returns "" when no date is selected, a string for point dates, and [string, string] for ranges.

Uncontrolled with a default value

// Point
<HotDate defaultValue="2026-06-13" onChange={(v) => console.log(v)} />

// Range
<HotDate
  dateType="range"
  defaultValue={["2026-01-01", "2026-01-31"]}
  onChange={(v) => console.log(v)}
/>

defaultValue sets the initial value on mount and immediately enters display mode, but the component is uncontrolled after that — React does not drive subsequent updates.

Imperative ref

import { useRef } from 'react';
import { HotDate, type HotDateHandle } from '@octax-app/hot-date-react';

const ref = useRef<HotDateHandle>(null);

<HotDate ref={ref} />

// Imperatively control the input:
ref.current?.focus();
ref.current?.blur();
ref.current?.clear();
console.log(ref.current?.value); // string | null

Event callbacks

<HotDate
  onFocus={(e) => console.log('focused', e)}
  onBlur={(e) => console.log('blurred', e)}
  onKeyDown={(e) => console.log('key', e.key)}
  onInput={(rawValue) => console.log('typing', rawValue)}
  onPaste={(e) => console.log('pasted')}
  onClick={(e) => console.log('clicked')}
  onMouseEnter={(e) => console.log('mouse in')}
  onMouseLeave={(e) => console.log('mouse out')}
  onError={(err) => console.log(err)} // "Date is outside the allowed range." | undefined
/>

Error handling

onError fires whenever the typed input is invalid — out of range, wrong mode, or unparseable. It fires with undefined when the error clears (user types a valid date or empties the field). Only fires when the error state actually changes, not on every keystroke.

<HotDate
  endDate={new Date()}              // today is the latest allowed date
  onError={(err) => {
    if (err) setErrorMsg(err);      // e.g. "Date is outside the allowed range."
    else setErrorMsg("");           // cleared
  }}
  onChange={(value) => {
    // value is "" when nothing is selected, "YYYY-MM-DD" when valid
    setValue(value as string);
  }}
/>

Props

| Prop | Type | Default | Description | | --- | --- | --- | --- | | value | string \| [string, string] \| null | — | Controlled value. Pass a string for point dates, [start, end] for ranges — same shape onChange returns. Renders in display mode while unfocused. | | defaultValue | string \| [string, string] \| null | — | Uncontrolled initial value. Same shape as value. Mounts in display mode; React does not drive updates after mount. | | onChange | (value: string \| [string, string]) => void | — | Fires on every valid parse. Returns "" when no value. Range returns [start, end]. | | onCommit | (value: string \| [string, string]) => void | — | Fires on Enter key commit. Returns "" when no value. | | onClear | () => void | — | Fires when input is cleared. | | onError | (error: string \| undefined) => void | — | Fires when input is invalid (out of range, unparseable, wrong mode). Fires undefined when the error clears. Only fires on state change, not every keystroke. | | onFocus | (e: FocusEvent) => void | — | Fires when the input gains focus. | | onBlur | (e: FocusEvent) => void | — | Fires when the input loses focus. | | onKeyDown | (e: KeyboardEvent) => void | — | Fires on keydown. | | onKeyUp | (e: KeyboardEvent) => void | — | Fires on keyup. | | onInput | (rawValue: string) => void | — | Fires on every keystroke with the raw typed string. | | onPaste | (e: ClipboardEvent) => void | — | Fires when content is pasted into the input. | | onClick | (e: MouseEvent) => void | — | Fires on click. | | onMouseEnter | (e: MouseEvent) => void | — | Fires when the mouse enters the component. | | onMouseLeave | (e: MouseEvent) => void | — | Fires when the mouse leaves the component. | | onMouseDown | (e: MouseEvent) => void | — | Fires on mousedown. | | onMouseUp | (e: MouseEvent) => void | — | Fires on mouseup. | | onMouseMove | (e: MouseEvent) => void | — | Fires on mousemove. | | format | string | "YYYY-MM-DD" | Output format. Tokens: YYYY YY MM DD M D MMM MMMM (case-insensitive). MMM/MMMM render locale-aware short/full month names. | | dateType | "point" \| "range" \| "combined" | "point" | "point" = single date only, "range" = range only, "combined" = both simultaneously (returns string or [string, string]). | | startDate | Date \| string | — | Minimum date. Accepts a JS Date or "YYYY-MM-DD" string. | | endDate | Date \| string | — | Maximum date. Accepts a JS Date or "YYYY-MM-DD" string. | | className | string | — | CSS class on the host element. | | style | React.CSSProperties | — | Inline styles on the host element. | | placeholder | string | "type anything..." | Input placeholder text. | | timezone | string | system timezone | IANA timezone (e.g. "America/New_York"). | | locale | string | navigator.language | BCP-47 locale (e.g. "en-US"). | | weekStart | "sunday" \| "monday" \| "tuesday" \| "wednesday" \| "thursday" \| "friday" \| "saturday" | "monday" | First day of week for relative expressions. | | disabled | boolean | false | Disable the input. | | required | boolean | false | Participates in form validation. | | autoFocus | boolean | false | Focus the input on mount. | | tabIndex | number | — | Sets the tab index on the inner <input>. Use -1 to remove from tab order. | | ref | React.Ref<HotDateHandle> | — | Imperative handle with focus(), blur(), clear(), and value. | | name | string | — | Form field name. | | showHint | boolean | true | Show the Tab autocomplete hint. | | error | boolean | false | Passes error: true into classNames functions. | | success | boolean | false | Passes success: true into classNames functions. | | classNames | ClassNamesConfig | — | Per-part class names. Each value is a string or (props) => string. Keys: input, ghost, hint. |

Output Format

| dateType | format not set | format="MM/DD/YYYY" | format="MMM DD, YYYY" | | --- | --- | --- | --- | | "point" | "2026-06-13" | "06/13/2026" | "Jun 13, 2026" | | "range" | ["2026-06-01", "2026-06-30"] | ["06/01/2026", "06/30/2026"] | ["Jun 01, 2026", "Jun 30, 2026"] | | "combined" | "2026-06-13" or ["2026-06-01", "2026-06-30"] | "06/13/2026" or ["06/01/2026", "06/30/2026"] | "Jun 13, 2026" or ["Jun 01, 2026", "Jun 30, 2026"] |

Empty / no selection always returns "" regardless of dateType or format.

Keyboard

| Key | Action | | --- | --- | | Tab | Accept the active autocomplete suggestion (pressing Tab again after accepting moves focus normally) | | Enter | Commit the current value | | / | Cycle through suggestions | | Escape | Reset active suggestion |

Styling with ::part()

The shadow DOM exposes these CSS parts:

| Part | Element | | --- | --- | | input | The <input> element | | ghost | The suggestion overlay | | hint | The Tab hint <kbd> chip |

hot-date::part(input) { font-size: 1rem; border-radius: 8px; }
hot-date::part(ghost) { padding: 0 0.75rem; }

Natural Language Examples

tomorrow
next friday
in 3 days
christmas
after tomorrow
jan to feb
this week
tomorrow to after tomorrow
between 5/15/2026 and 6/13/2026
3 days before christmas
9 days after christmas until new years

Credits

Built on top of @stolinski/hot-date by Scott Tolinski — the natural language date parsing engine and web component that powers this React wrapper.

License

MIT