Skip to content
Devix Open Source

Guide

Getting started

npm install @devix-labs/time-picker
<label for="at">Appointment</label>
<input id="at" name="at" value="09:30">
import { createTimePicker } from '@devix-labs/time-picker';
import '@devix-labs/time-picker/styles.css';

const picker = createTimePicker(document.querySelector('#at'), {
  min: '09:00',
  max: '17:00',
  step: 30,
  disabled: [['12:00', '13:00']],   // lunch
  onChange: (value) => console.log(value),   // "14:30"
});

The input you point at keeps its label and posts the time under its own name, as HH:mm. It shows the time the way the person's locale writes it — 5:30 PM in Chicago, 17:30 in Manchester, ٥:٣٠ م in Dubai — while the value your server receives is always the same 24-hour string.

The value

Always "HH:mm", or "HH:mm:ss" with seconds: true. Never a Date: a Date is an instant, so "09:00" becomes a different time when a server in another zone reads it, and it moves an hour twice a year. Here a time is just a time.

picker.getValue();   // "14:30"
picker.getTime();    // 52200 — seconds since midnight, for arithmetic
picker.setValue('09:15');
picker.setValue(null);

Which times are on offer

createTimePicker(input, {
  min: '09:00',
  max: '17:00',
  step: 30,                              // minutes
  disabled: [
    ['12:00', '13:00'],                  // a span
    '15:30',                             // one time
    (seconds) => seconds % 3600 !== 0,   // or a rule of your own
  ],
});

A span that runs backwards crosses midnight, which is how a kitchen that shuts at eleven is written:

createTimePicker(input, { disabled: [['23:00', '06:00']] });

Unavailable times are shown struck through rather than hidden. A list with the middle missing tells nobody anything; a list with "12:30" crossed out says the place is shut for lunch.

enabled is the other way round — an allow-list — and disabled still overrides it.

Booking, where each slot is different

createTimePicker(input, {
  slots: [
    { time: '09:00', note: '3 left' },
    { time: '09:30', disabled: true, reason: 'fully booked' },
    { time: '10:00', note: 'AED 250', className: 'is-premium' },
  ],
});

reason is read out by a screen reader, so the row is not just mysteriously dead.

Typing

The field is a real text input, so a phone keyboard, a paste and a password manager all work. It takes what people actually type:

5pm   5 PM   5p   17:30   1730   530   5.30   5:3   7h30   noon   midnight   now
٥:٣٠ م   ۱۷:۳۰

When "5" could be either five, the picker asks which one it would accept. In a shop open from nine to six, 5 can only mean the afternoon — so that is what you get. Where both work, it stays in the half of the day the field is already in.

Enter applies the time. It does not submit the form the picker is sitting in.

A range

import { createTimeRangePicker } from '@devix-labs/time-picker';

createTimeRangePicker(document.querySelector('#opens'), {
  endInput: document.querySelector('#closes'),
  step: 30,
  minDuration: 60,
  onChange: ({ start, end }) => save(start, end),
});

Picking the start moves you on to the end field, ends that would make the range too short are already struck out, and the panel says how long it is. overnight: true lets a shift run past midnight; getDuration() returns its length in seconds either way.

Where to go next

Updated 15 Sep 2026