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

@poursha98/react-ios-time-picker

v2.0.3

Published

iOS-style wheel picker component for React with smooth scroll-snap, accessibility, 12-hour AM/PM support, and shadcn-style compound components for full customization.

Readme

react-ios-time-picker

A beautiful, accessible iOS-style time picker for React with Tailwind CSS and shadcn-style compound components. Styled with Tailwind out of the box—no CSS import needed. Features smooth scroll-snap physics, 12-hour AM/PM support, touch/mouse drag, RTL support, and full TypeScript types.

npm bundle size license

✨ Features

  • 🎡 iOS-style scroll physics - Native scroll-snap behavior
  • ♾️ Infinite Scrolling - Seamless looping of wheels (optional)
  • 🧩 Compound components - Mix and match parts like shadcn/ui
  • 🕐 12-hour AM/PM support - Built-in period wheel
  • 🖱️ Multi-input support - Touch, mouse drag, and keyboard
  • Accessible - ARIA listbox pattern with full keyboard support
  • 🌐 RTL & Persian numerals - Built-in support for Persian/Arabic
  • 🎨 Tailwind-first - Styled with Tailwind CSS, no CSS import needed
  • 🎨 Fully customizable - Override styles easily via className prop
  • 📦 Lightweight - ~5KB gzipped
  • 🔧 TypeScript - Complete type definitions included

Note: Requires Tailwind CSS v3.0+ in your project

📦 Installation

npm install @poursha98/react-ios-time-picker

Requirements: Tailwind CSS v3.0+ must be installed.

🚀 Quick Start

Simple Usage (All-in-One)

Simple Usage

import { useState } from "react";
import { TimePicker } from "@poursha98/react-ios-time-picker";
// No CSS import needed! ✨

function App() {
  const [time, setTime] = useState("09:30");

  return (
    <TimePicker
      value={time}
      onChange={setTime}
      onConfirm={() => console.log("Selected:", time)}
    />
  );
}

12-Hour Format with AM/PM

12-Hour Usage

import { useState } from "react";
import { TimePicker } from "@poursha98/react-ios-time-picker";

function App() {
  const [time, setTime] = useState("02:30 PM");

  return <TimePicker value={time} onChange={setTime} is12Hour />;
}

Infinite Scrolling (Loop)

Enable infinite looping for wheels (except AM/PM):

<TimePicker value={time} onChange={setTime} loop />

Customizing Styles

Easily override default styles with your own Tailwind classes:

<TimePicker
  value={time}
  onChange={setTime}
  className="bg-slate-900 p-8 rounded-3xl shadow-2xl" // Override container
/>

Or use compound components for granular control:

<TimePickerRoot
  value={time}
  onChange={setTime}
  className="bg-linear-to-br from-purple-500 to-pink-500 p-8"
>
  <TimePickerTitle className="text-white text-2xl">Pick a Time</TimePickerTitle>
  <TimePickerWheels>
    <TimePickerWheel type="hour" className="bg-white/20 backdrop-blur" />
    <TimePickerSeparator className="text-white">:</TimePickerSeparator>
    <TimePickerWheel type="minute" className="bg-white/20 backdrop-blur" />
  </TimePickerWheels>
  <TimePickerButton className="bg-white text-purple-600 hover:bg-gray-100">
    Confirm
  </TimePickerButton>
</TimePickerRoot>

Customizing Wheel Item Colors

Customize the colors of wheel items using the classNames prop:

<TimePickerWheel
  type="hour"
  classNames={{
    item: "text-gray-400", // Unselected items
    selectedItem: "text-primary", // Selected item
  }}
/>

You can also use Tailwind's arbitrary variant syntax to target data attributes:

<TimePickerWheel
  type="minute"
  className="[&_[data-wheel-item]]:text-gray-400 [&_[data-wheel-item][data-selected]]:text-blue-500"
/>

Compound Components (Full Control)

Compound Components

For maximum flexibility, use individual compound components:

import { useState } from "react";
import {
  TimePickerRoot,
  TimePickerTitle,
  TimePickerWheels,
  TimePickerWheel,
  TimePickerSeparator,
  TimePickerButton,
} from "@poursha98/react-ios-time-picker";

function CustomTimePicker() {
  const [time, setTime] = useState("09:30");

  return (
    <TimePickerRoot
      value={time}
      onChange={setTime}
      className="bg-slate-900 rounded-2xl p-6"
    >
      <TimePickerTitle className="text-white text-xl font-bold mb-4">
        ⏰ Select Time
      </TimePickerTitle>

      <TimePickerWheels className="flex justify-center items-center gap-2">
        <TimePickerWheel type="hour" className="bg-slate-800 rounded-lg" />

        <TimePickerSeparator className="text-blue-400 text-2xl font-bold">
          :
        </TimePickerSeparator>

        <TimePickerWheel type="minute" className="bg-slate-800 rounded-lg" />
      </TimePickerWheels>

      <TimePickerButton className="mt-6 w-full bg-blue-500 text-white py-3 rounded-xl">
        Confirm Selection
      </TimePickerButton>
    </TimePickerRoot>
  );
}

12-Hour with Compound Components

<TimePickerRoot value={time} onChange={setTime} is12Hour>
  <TimePickerWheels>
    <TimePickerWheel type="hour" />
    <TimePickerSeparator>:</TimePickerSeparator>
    <TimePickerWheel type="minute" />
    <TimePickerWheel type="period" /> {/* AM/PM wheel */}
  </TimePickerWheels>
  <TimePickerButton />
</TimePickerRoot>

Low-Level Wheel (Custom Pickers)

Build completely custom pickers using the base Wheel component:

import { useState } from "react";
import { Wheel } from "@poursha98/react-ios-time-picker";

const fruits = [
  "🍎 Apple",
  "🍊 Orange",
  "🍋 Lemon",
  "🍇 Grape",
  "🍓 Strawberry",
];

function FruitPicker() {
  const [selected, setSelected] = useState(0);

  return (
    <Wheel
      items={fruits}
      value={selected}
      onChange={setSelected}
      itemHeight={48}
      visibleCount={5}
    />
  );
}

📚 API Reference

TimePicker Props (All-in-One)

| Prop | Type | Default | Description | | ------------------- | ------------------------ | -------------------- | ----------------------------------------- | | value | string | required | Time value ("HH:MM" or "HH:MM AM/PM") | | onChange | (time: string) => void | required | Called when time changes | | onConfirm | () => void | - | Called when confirm button is clicked | | is12Hour | boolean | false | Enable 12-hour format with AM/PM | | numerals | "en" \| "fa" \| "auto" | "auto" | Number format and text language | | hours | number[] | [0-23] or [1-12] | Custom hours array | | minutes | number[] | [0-59] | Custom minutes array | | minuteStep | number | - | Minute interval (5, 15, 30) | | showTitle | boolean | true | Show title | | showLabels | boolean | true | Show hour/minute labels | | showConfirmButton | boolean | true | Show confirm button | | itemHeight | number | 48 | Height of each wheel item | | visibleCount | number | 5 | Number of visible items | | loop | boolean | false | Enable infinite looping | | disabled | boolean | false | Disable the picker | | className | string | - | Root element className | | classNames | TimePickerClassNames | - | CSS class names (legacy) | | styles | TimePickerStyles | - | Inline styles (legacy) |

Compound Components

TimePickerRoot

The context provider that wraps all other components.

<TimePickerRoot
  value={time}
  onChange={setTime}
  is12Hour={false}
  numerals="auto"
  disabled={false}
  loop={false}
  onConfirm={() => {}}
  className="my-picker"
>
  {children}
</TimePickerRoot>

TimePickerTitle

Displays the picker title. Uses <h2> by default.

<TimePickerTitle className="text-xl font-bold">Select Time</TimePickerTitle>;

{
  /* Use asChild for custom elements */
}
<TimePickerTitle asChild>
  <h1>Choose Time</h1>
</TimePickerTitle>;

TimePickerWheels

Container for the wheel columns.

<TimePickerWheels className="flex gap-2">
  {/* wheels go here */}
</TimePickerWheels>

TimePickerWheel

Individual wheel for hour, minute, or period (AM/PM).

<TimePickerWheel type="hour" className="w-20" />
<TimePickerWheel type="minute" className="w-20" />
<TimePickerWheel type="period" className="w-16" /> {/* AM/PM */}

TimePickerSeparator

The colon between wheels.

<TimePickerSeparator className="text-blue-500">:</TimePickerSeparator>

TimePickerLabel

Labels for each wheel column.

<TimePickerLabel type="hour">Hour</TimePickerLabel>
<TimePickerLabel type="minute">Minute</TimePickerLabel>

TimePickerButton

Confirm button that triggers onConfirm.

<TimePickerButton className="bg-blue-500 text-white px-4 py-2 rounded">
  Confirm
</TimePickerButton>;

{
  /* Use asChild for custom elements */
}
<TimePickerButton asChild>
  <a href="/next">Continue</a>
</TimePickerButton>;

Wheel Props

| Prop | Type | Default | Description | | -------------- | ---------------------------------------- | ------------ | ----------------------------- | | items | T[] | required | Array of items to display | | value | number | required | Currently selected index | | onChange | (index: number) => void | required | Called when selection changes | | itemHeight | number | 40 | Height of each item in pixels | | visibleCount | number | 5 | Number of visible items | | width | string \| number | "100%" | Width of the wheel | | loop | boolean | false | Enable infinite looping | | renderItem | (item, index, isSelected) => ReactNode | - | Custom item renderer | | disabled | boolean | false | Disable the wheel | | classNames | WheelClassNames | - | CSS class names | | styles | WheelStyles | - | Inline styles | | aria-label | string | - | Accessible label | | getItemLabel | (item, index) => string | - | Accessible item labels |

🎨 Styling

CSS Variables

Customize colors using CSS variables:

:root {
  --time-picker-bg: #ffffff;
  --time-picker-text: #1f2937;
  --time-picker-text-secondary: #9ca3af;
  --time-picker-primary: #3b82f6;
  --time-picker-primary-light: rgba(59, 130, 246, 0.1);
}

/* Dark mode */
.dark {
  --time-picker-bg: #1f2937;
  --time-picker-text: #f3f4f6;
  --time-picker-text-secondary: #9ca3af;
  --time-picker-primary: #60a5fa;
  --time-picker-primary-light: rgba(96, 165, 250, 0.1);
}

Data Attribute Selectors

Style using data attributes for more specificity:

/* Root */
[data-time-picker] {
  background: #1e293b;
}

/* Title */
[data-time-picker-title] {
  color: white;
}

/* Wheel items - no default colors, customize as needed */
[data-wheel-item] {
  color: #94a3b8; /* Unselected items */
}

[data-wheel-item][data-selected] {
  color: #3b82f6; /* Selected item */
  font-weight: bold;
}

/* Wheel indicator */
[data-wheel-indicator] {
  border-color: #3b82f6;
}

Note: As of version 2.0, wheel items no longer have hardcoded text colors. You must explicitly set colors using the classNames prop, data attribute selectors, or the className prop on TimePickerWheel.

Tailwind CSS Example

<TimePickerRoot
  value={time}
  onChange={setTime}
  className="bg-slate-900 rounded-2xl p-6 shadow-xl"
>
  <TimePickerTitle className="text-white font-bold text-xl mb-4">
    Choose Time
  </TimePickerTitle>

  <TimePickerWheels className="flex justify-center gap-2">
    <TimePickerWheel type="hour" className="bg-slate-800 rounded-lg" />
    <TimePickerSeparator className="text-blue-400 text-2xl">
      :
    </TimePickerSeparator>
    <TimePickerWheel type="minute" className="bg-slate-800 rounded-lg" />
  </TimePickerWheels>

  <TimePickerButton className="mt-4 w-full bg-linear-to-r from-blue-500 to-purple-500 text-white py-3 rounded-xl hover:opacity-90 transition">
    Confirm
  </TimePickerButton>
</TimePickerRoot>

📝 Examples

Persian/RTL Support

<TimePicker
  value={time}
  onChange={setTime}
  numerals="fa" // Persian numerals + Persian text
/>

Custom Minute Steps

// 15-minute intervals
<TimePicker
  value={time}
  onChange={setTime}
  minuteStep={15}
/>

// Or custom array
<TimePicker
  value={time}
  onChange={setTime}
  minutes={[0, 15, 30, 45]}
/>

React Hook Form Integration

import { Controller, useForm } from "react-hook-form";
import { TimePicker } from "@poursha98/react-ios-time-picker";

function MyForm() {
  const { control, handleSubmit } = useForm({
    defaultValues: { appointmentTime: "09:00" },
  });

  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <Controller
        name="appointmentTime"
        control={control}
        render={({ field }) => (
          <TimePicker
            value={field.value}
            onChange={field.onChange}
            showConfirmButton={false}
          />
        )}
      />
      <button type="submit">Book Appointment</button>
    </form>
  );
}

Without Default Styles

Import only the components without any CSS:

import {
  TimePickerRoot,
  TimePickerWheel,
  TimePickerButton,
} from "@poursha98/react-ios-time-picker";
// No CSS import!

function MinimalPicker() {
  const [time, setTime] = useState("09:30");

  return (
    <TimePickerRoot value={time} onChange={setTime}>
      <TimePickerWheel type="hour" />
      <span>:</span>
      <TimePickerWheel type="minute" />
      <TimePickerButton>OK</TimePickerButton>
    </TimePickerRoot>
  );
}

♿ Accessibility

  • Full keyboard navigation: Arrow keys, Home, End, Page Up/Down
  • ARIA listbox pattern with option roles
  • Screen reader announcements via aria-label and getItemLabel
  • Focus management and visible focus indicators
  • Unique ARIA IDs for multiple instances

🌐 Browser Support

  • Chrome, Edge, Safari, Firefox (latest 2 versions)
  • iOS Safari 13+
  • Android Chrome 80+

📄 License

MIT © Poursha98