timepicker-ui

Modern time picker library built with TypeScript. Works with any framework or vanilla JavaScript.

npm version downloads license size

Live DemoDocumentationReact WrapperChangelog

Upgrading from v3? Check the upgrade guide below. Upgrading from v2? Check the v2 → v3 upgrade guide.

Features

Known Limitations

This project is actively maintained. Some areas planned for improvement:

Contributions welcome! Feel free to open an issue or PR.

Installation

npm install timepicker-ui

Important: Global CSS Required

Your app needs this global CSS rule for correct styling:

*,
*::before,
*::after {
  box-sizing: border-box;
}

Most projects include this by default.

Quick Start

Basic Usage

<input id="timepicker" type="text" />
import { TimepickerUI } from "timepicker-ui";
import "timepicker-ui/main.css";

const input = document.querySelector("#timepicker");
const picker = new TimepickerUI(input);
picker.create();

With Options

const picker = new TimepickerUI(input, {
  ui: {
    theme: "dark",
    animation: true,
    backdrop: true,
  },
  clock: {
    type: "24h",
  },
  callbacks: {
    onConfirm: (data) => {
      console.log("Selected time:", data);
    },
  },
});
picker.create();

React Example

import { useEffect, useRef } from "react";
import { TimepickerUI } from "timepicker-ui";
import "timepicker-ui/main.css";

function TimePickerComponent() {
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (inputRef.current) {
      const picker = new TimepickerUI(inputRef.current, {
        callbacks: {
          onConfirm: (data) => {
            console.log("Time selected:", data);
        },
      });
      picker.create();

      return () => picker.destroy();
    }
  }, []);

  return <input ref={inputRef} type="text" />;
}

Configuration

Full documentation available at timepicker-ui.vercel.app/docs

Options Structure (v4.0.0 Breaking Change)

Options are now organized into 5 logical groups:

const picker = new TimepickerUI(input, {
  clock: ClockOptions, // Clock behavior (type, increments, disabled time)
  ui: UIOptions, // Appearance (theme, animation, mobile, inline)
  labels: LabelsOptions, // Text labels (AM/PM, buttons, headers)
  behavior: BehaviorOptions, // Behavior (focus, delays, ID)
  callbacks: CallbacksOptions, // Event handlers
});

Clock Options

Property Type Default Description
type 12h / 24h 12h Clock format
incrementHours number 1 Hour increment step
incrementMinutes number 1 Minute increment step
autoSwitchToMinutes boolean false Auto-switch after hour selected
disabledTime object undefined Disable specific hours/minutes
currentTime boolean/object undefined Set current time to input

UI Options

Property Type Default Description
theme string basic Theme (11 themes available)
animation boolean true Enable animations
backdrop boolean true Show backdrop overlay
mobile boolean false Force mobile version
enableSwitchIcon boolean false Show desktop/mobile switch icon
editable boolean false Allow manual input editing
enableScrollbar boolean false Enable scroll when picker open
cssClass string undefined Additional CSS class
appendModalSelector string "" Custom container selector
iconTemplate string SVG Desktop switch icon template
iconTemplateMobile string SVG Mobile switch icon template
inline object undefined Inline mode configuration

Labels Options

Property Type Default Description
am string AM AM label text
pm string PM PM label text
ok string OK OK button text
cancel string Cancel Cancel button text
time string Select time Desktop time label
mobileTime string Enter Time Mobile time label
mobileHour string Hour Mobile hour label
mobileMinute string Minute Mobile minute label

Behavior Options

Property Type Default Description
focusInputAfterClose boolean false Focus input after close
focusTrap boolean true Trap focus in modal
delayHandler number 300 Click delay (ms)
id string auto-generated Custom instance ID

Callbacks Options

Property Type Description
onConfirm function Time confirmed
onCancel function Cancelled
onOpen function Picker opened
onUpdate function Time updated (real-time)
onSelectHour function Hour selected
onSelectMinute function Minute selected
onSelectAM function AM selected
onSelectPM function PM selected
onError function Error occurred

Migration from v3.x to v4.0.0

All options must be moved into groups:

// v3.x (DEPRECATED)
-const picker = new TimepickerUI(input, {
-  clockType: '24h',
-  theme: 'dark',
-  animation: true,
-  incrementMinutes: 5,
-  amLabel: 'AM',
-  onConfirm: (data) => {}
-});

// v4.0.0 (NEW)
+const picker = new TimepickerUI(input, {
+  clock: {
+    type: '24h',
+    incrementMinutes: 5
+  },
+  ui: {
+    theme: 'dark',
+    animation: true
+  },
+  labels: {
+    am: 'AM'
+  },
+  callbacks: {
+    onConfirm: (data) => {}
+  }
+});

Full migration table:

v3.x (flat) v4.0.0 (grouped)
clockType clock.type
incrementHours clock.incrementHours
incrementMinutes clock.incrementMinutes
autoSwitchToMinutes clock.autoSwitchToMinutes
disabledTime clock.disabledTime
currentTime clock.currentTime
theme ui.theme
animation ui.animation
backdrop ui.backdrop
mobile ui.mobile
enableSwitchIcon ui.enableSwitchIcon
editable ui.editable
enableScrollbar ui.enableScrollbar
cssClass ui.cssClass
appendModalSelector ui.appendModalSelector
iconTemplate ui.iconTemplate
iconTemplateMobile ui.iconTemplateMobile
inline ui.inline
amLabel labels.am
pmLabel labels.pm
okLabel labels.ok
cancelLabel labels.cancel
timeLabel labels.time
mobileTimeLabel labels.mobileTime
hourMobileLabel labels.mobileHour
minuteMobileLabel labels.mobileMinute
focusInputAfterCloseModal behavior.focusInputAfterClose
focusTrap behavior.focusTrap
delayHandler behavior.delayHandler
id behavior.id
onConfirm callbacks.onConfirm
onCancel callbacks.onCancel
onOpen callbacks.onOpen
onUpdate callbacks.onUpdate
onSelectHour callbacks.onSelectHour
onSelectMinute callbacks.onSelectMinute
onSelectAM callbacks.onSelectAM
onSelectPM callbacks.onSelectPM
onError callbacks.onError

Themes

Available themes: basic, crane, crane-straight, m3-green, m2, dark, glassmorphic, pastel, ai, cyberpunk

import "timepicker-ui/main.css"; // Required base styles
import "timepicker-ui/theme-dark.css"; // Specific theme

const picker = new TimepickerUI(input, {
  ui: {
    theme: "dark",
  },
});

Disabled Time

const picker = new TimepickerUI(input, {
  clock: {
    disabledTime: {
      hours: [1, 3, 5, 8],
      minutes: [15, 30, 45],
      interval: "10:00 AM - 2:00 PM",
    },
  },
});

Inline Mode

const picker = new TimepickerUI(input, {
  ui: {
    inline: {
      enabled: true,
      containerId: "timepicker-container",
      showButtons: false,
      autoUpdate: true,
    },
  },
});

API Methods

Instance Methods

const picker = new TimepickerUI(input, options);

picker.create(); // Initialize
picker.open(); // Open programmatically
picker.close(); // Close
picker.destroy(); // Clean up
picker.getValue(); // Get current time
picker.setValue("14:30"); // Set time
picker.update({ options }); // Update configuration

Static Methods

TimepickerUI.getById("my-id"); // Get instance by ID
TimepickerUI.getAllInstances(); // Get all instances
TimepickerUI.destroyAll(); // Destroy all instances

Events

Listen to timepicker events using the EventEmitter API (v4+) or callback options:

const picker = new TimepickerUI(input);
picker.create();

picker.on("confirm", (data) => {
  console.log("Time confirmed:", data);
});

picker.on("cancel", (data) => {
  console.log("Cancelled:", data);
});

picker.on("open", () => {
  console.log("Picker opened");
});

picker.on("update", (data) => {
  console.log("Time updated:", data);
});

picker.on("select:hour", (data) => {
  console.log("Hour selected:", data.hour);
});

picker.on("select:minute", (data) => {
  console.log("Minute selected:", data.minutes);
});

picker.on("select:am", (data) => {
  console.log("AM selected");
});

picker.on("select:pm", (data) => {
  console.log("PM selected");
});

picker.on("error", (data) => {
  console.log("Error:", data.error);
});

picker.once("confirm", (data) => {
  console.log("This runs only once");
});

picker.off("confirm", handler);

Legacy DOM Events (v3.x Only)

⚠️ Only Available in v3.x - Completely Removed in v4.0.0

DOM events (e.g., timepicker:confirm) were removed in v4.0.0. Use the EventEmitter API shown above or callback options instead.

v3.x code (no longer works in v4):

input.addEventListener("timepicker:confirm", (e) => {
  console.log("Time:", e.detail);
});

v4.0.0 alternatives:

// Option 1: EventEmitter API
picker.on("confirm", (data) => {
  console.log("Time:", data);
});

// Option 2: Callback options
const picker = new TimepickerUI(input, {
  callbacks: {
    onConfirm: (data) => {
      console.log("Time:", data);
    },
  },
});

Removed events: timepicker:open, timepicker:cancel, timepicker:confirm, timepicker:update, timepicker:select-hour, timepicker:select-minute, timepicker:select-am, timepicker:select-pm, timepicker:error

Upgrade Guide: v3 → v4

Breaking Changes

1. CSS Class Names Renamed

All CSS classes have been shortened from timepicker-ui-* to tp-ui-*:

/* v3 */
.timepicker-ui-wrapper {
}
.timepicker-ui-modal {
}
.timepicker-ui-clock-face {
}
.timepicker-ui-hour {
}
.timepicker-ui-minutes {
}
.timepicker-ui-am {
}
.timepicker-ui-pm {
}

/* v4 */
.tp-ui-wrapper {
}
.tp-ui-modal {
}
.tp-ui-clock-face {
}
.tp-ui-hour {
}
.tp-ui-minutes {
}
.tp-ui-am {
}
.tp-ui-pm {
}

Impact: If you have custom CSS targeting timepicker elements, update all class selectors.

2. Grouped Options Structure

All options are now organized into logical groups for better maintainability:

// v3
const picker = new TimepickerUI(input, {
  clockType: "12h",
  theme: "dark",
  enableSwitchIcon: true,
  mobile: true,
  animation: true,
  backdrop: true,
  focusTrap: true,
  editable: true,
  onConfirm: (data) => console.log(data),
});

// v4 - Grouped options
const picker = new TimepickerUI(input, {
  clock: {
    type: "12h",
    incrementHours: 1,
    incrementMinutes: 1,
    currentTime: { time: new Date(), updateInput: true },
    disabledTime: { hours: [1, 2, 3] },
    autoSwitchToMinutes: false,
  },
  ui: {
    theme: "dark",
    enableSwitchIcon: true,
    mobile: true,
    animation: true,
    backdrop: true,
    editable: true,
    cssClass: "my-custom-class",
    inline: { enabled: false },
  },
  labels: {
    time: "Select Time",
    am: "AM",
    pm: "PM",
    ok: "OK",
    cancel: "Cancel",
  },
  behavior: {
    focusTrap: true,
    focusInputAfterClose: false,
    delayHandler: 300,
  },
  callbacks: {
    onConfirm: (data) => console.log(data),
    onCancel: (data) => console.log(data),
    onOpen: (data) => console.log(data),
    onUpdate: (data) => console.log(data),
    onSelectHour: (data) => console.log(data),
    onSelectMinute: (data) => console.log(data),
    onSelectAM: (data) => console.log(data),
    onSelectPM: (data) => console.log(data),
    onError: (data) => console.log(data),
  },
});

2. Legacy DOM Events Removed

DOM events have been completely removed. Use EventEmitter API or callback options:

// v3 - Deprecated (removed in v4)
input.addEventListener("timepicker:confirm", (e) => {
  console.log(e.detail);
});

// v4 - EventEmitter API (recommended)
picker.on("confirm", (data) => {
  console.log(data);
});

// v4 - Or use callback options
const picker = new TimepickerUI(input, {
  callbacks: {
    onConfirm: (data) => console.log(data),
  },
});

3. setTheme() Method Removed

Programmatic theme setting via setTheme() has been removed. Use CSS classes instead:

// v3 - Removed in v4
picker.setTheme({
  primaryColor: "#ff0000",
  backgroundColor: "#000000",
});

// v4 - Use CSS classes with CSS variables
const picker = new TimepickerUI(input, {
  ui: { cssClass: "my-custom-theme" },
});
/* Define custom theme in CSS */
.tp-ui-wrapper.my-custom-theme {
  --tp-primary: #ff0000;
  --tp-bg: #000000;
  --tp-surface: #f0f0f0;
}

4. SSR-Safe Architecture

All modules are now SSR-safe and can be imported in Node.js environments without crashing.

Migration Steps

  1. Update option structure - Move options into clock, ui, labels, behavior, callbacks groups
  2. Replace DOM events - Switch to picker.on() EventEmitter API or callback options
  3. Remove setTheme() calls - Use CSS classes with CSS variable overrides
  4. Test SSR compatibility - If using Next.js/Nuxt/Remix, verify hydration works correctly

New in v4

Bundle Size Comparison


Upgrade Guide: v2 → v3

Breaking Changes

1. CSS must be imported manually

// v3 - Required
import "timepicker-ui/main.css";

2. Event names changed

// v2
input.addEventListener("show", ...);
input.addEventListener("accept", ...);

// v3
input.addEventListener("timepicker:open", ...);
input.addEventListener("timepicker:confirm", ...);

3. Use callbacks instead of events

// v3 - Recommended
const picker = new TimepickerUI(input, {
  onConfirm: (data) => console.log(data),
  onCancel: (data) => console.log("Cancelled"),
});

4. destroy() behavior

// v2 - Removed input from DOM
picker.destroy();

// v3 - Only destroys picker, keeps input
picker.destroy();

New in v3

Migration to EventEmitter (v3.x)

Starting in v3.x, we recommend using the new EventEmitter API instead of DOM events:

// Old way (deprecated, will be removed in v4)
input.addEventListener("timepicker:confirm", (e) => {
  console.log(e.detail);
});

// New way (recommended)
picker.on("confirm", (data) => {
  console.log(data);
});

Benefits:

Framework Integration

React

import { useEffect, useRef } from "react";
import { TimepickerUI } from "timepicker-ui";
import "timepicker-ui/main.css";

function App() {
  const inputRef = useRef(null);

  useEffect(() => {
    if (!inputRef.current) return;
    const picker = new TimepickerUI(inputRef.current);
    picker.create();
    return () => picker.destroy();
  }, []);

  return <input ref={inputRef} placeholder="Select time" />;
}

Vue 3

<template>
  <input ref="pickerInput" placeholder="Select time" />
</template>

<script setup>
import { onMounted, ref } from "vue";
import { TimepickerUI } from "timepicker-ui";
import "timepicker-ui/main.css";

const pickerInput = ref(null);

onMounted(() => {
  if (!pickerInput.value) return;
  const picker = new TimepickerUI(pickerInput.value);
  picker.create();
});
</script>

Angular

import { Component, ElementRef, ViewChild, AfterViewInit } from "@angular/core";
import { TimepickerUI } from "timepicker-ui";

@Component({
  selector: "app-root",
  template: `<input #timepickerInput placeholder="Select time" />`,
})
export class App implements AfterViewInit {
  @ViewChild("timepickerInput") inputRef!: ElementRef<HTMLInputElement>;

  ngAfterViewInit() {
    const picker = new TimepickerUI(this.inputRef.nativeElement);
    picker.create();
  }
}

Add to angular.json styles:

"styles": ["src/styles.css", "timepicker-ui/main.css"]

Development

Development tooling is in the app/ directory. See app/README.md for details.

License

MIT © Piotr Glejzer

Contributing

Contributions welcome! Check the issues page.

Browser Support

Chrome 60+, Firefox 55+, Safari 12+, Edge 79+, iOS Safari 12+, Chrome Android 60+

Support