Skip to content
Example · Features

Open From a Button

Open the picker from an icon button next to the field. The field stays a normal text input, so the time can also be typed.

Version

This setup needs timepicker-ui 4.5.0 or later.

Icon on the Right (12h)

Pass the wrapper to the constructor, not the input. The wrapper becomes the picker root, and the data-open button inside it opens the picker. A click on the field does not.

Preview

HTML

index.htmlhtml
<label for="meeting-time">Meeting time</label>
<div class="time-field" id="meeting-time-field">
<input id="meeting-time" type="text" />
<button type="button" data-open="meeting-time" aria-label="Choose time">
<svg aria-hidden="true"><!-- clock icon --></svg>
</button>
</div>

JavaScript

index.tstypescript
import { TimepickerUI } from 'timepicker-ui';
const wrapper = document.querySelector('#meeting-time-field');
const picker = new TimepickerUI(wrapper, {
behavior: { returnFocus: true }
});
picker.create();

Icon on the Left (24h)

The same setup with clock: { type: '24h' }. The button comes before the input in the markup, so the Tab order matches what you see.

Preview

HTML

index.htmlhtml
<label for="departure-time">Departure time</label>
<div class="time-field time-field--icon-left" id="departure-time-field">
<button type="button" data-open="departure-time" aria-label="Choose time">
<svg aria-hidden="true"><!-- clock icon --></svg>
</button>
<input id="departure-time" type="text" />
</div>

JavaScript

index.tstypescript
import { TimepickerUI } from 'timepicker-ui';
const wrapper = document.querySelector('#departure-time-field');
const picker = new TimepickerUI(wrapper, {
clock: { type: '24h' },
behavior: { returnFocus: true }
});
picker.create();

Placing the Icon

Position the button with your own CSS. For example:

index.csscss
.time-field {
position: relative;
}
.time-field input {
width: 100%;
padding-right: 3rem;
}
.time-field button {
position: absolute;
top: 50%;
right: 0.25rem;
transform: translateY(-50%);
}
.time-field--icon-left input {
padding-right: 1rem;
padding-left: 3rem;
}
.time-field--icon-left button {
right: auto;
left: 0.25rem;
}

React

Put a ref on the wrapper and create the picker in an effect, so it only runs in the browser:

index.tsxtsx
import { useEffect, useId, useRef } from 'react';
import { TimepickerUI } from 'timepicker-ui';
import { Clock } from 'lucide-react';
export function MeetingTimeField() {
const id = useId();
const wrapperRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!wrapperRef.current) return;
const picker = new TimepickerUI(wrapperRef.current, {
behavior: { returnFocus: true }
});
picker.create();
return () => picker.destroy();
}, []);
return (
<>
<label htmlFor={id}>Meeting time</label>
<div ref={wrapperRef} className="time-field">
<input id={id} type="text" />
<button type="button" data-open="meeting-time" aria-label="Choose time">
<Clock aria-hidden="true" />
</button>
</div>
</>
);
}

Notes

  • Only the button opens the picker. The field can be typed in, and the picker reads the typed value when it opens.
  • Use a real <button type="button">. It also opens the picker with Enter and Space, and inside a form type="button" keeps it from submitting the form.
  • An icon-only button has no text, so give it an aria-label such as "Choose time".
  • behavior.returnFocus: true moves focus back to the field after the picker closes.
  • Every element in the wrapper with the same data-open value as the first one opens the picker.