Skip to content
Devix Open Source

Guide

React, Vue, Svelte and a plain page

Every wrapper is a thin layer over the same DOM core, so all of them behave identically.

React

import { TimePicker } from '@devix-labs/time-picker/react';
import '@devix-labs/time-picker/styles.css';

function Booking() {
  const [at, setAt] = useState('09:30');

  return (
    <TimePicker
      value={at}
      onChange={setAt}
      name="at"
      min="09:00"
      max="17:00"
      step={30}
      disabled={[['12:00', '13:00']]}
    />
  );
}

Controlled or uncontrolled: pass value and it follows, or use defaultValue and read the time from onChange. Anything that is not an option — className, placeholder, required, aria-* — goes straight to the input. disabled={true} disables the field; disabled={[...]} is the list of times.

A range:

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

<TimeRangePicker
  value={hours}
  onChange={setHours}
  minDuration={60}
  step={30}
  name="opens"
  endProps={{ name: 'closes', 'aria-label': 'Closes' }}
/>

Both render plain <input> elements, so they are safe to server-render; the widget is built when the effect runs. A ref gives you open(), close(), focus() and instance.

Vue

<script setup>
import { onMounted, onBeforeUnmount, ref } from 'vue';
import { timeField } from '@devix-labs/time-picker/vue';
import '@devix-labs/time-picker/styles.css';

const props = defineProps({ modelValue: String });
const emit = defineEmits(['update:modelValue']);
const host = ref(null);
let picker;

onMounted(() => {
  picker = timeField(host.value, {
    step: 30,
    value: props.modelValue,
    onChange: (value) => emit('update:modelValue', value),
  });
});
onBeforeUnmount(() => picker?.destroy());
</script>

<template>
  <input ref="host">
</template>

Svelte

<script>
  import { time } from '@devix-labs/time-picker/svelte';
  import '@devix-labs/time-picker/styles.css';

  let at = '09:30';
</script>

<input use:time={{ value: at, step: 30, min: '09:00', onChange: (v) => (at = v) }}>

A web component

<script type="module" src="https://devix.pk/cdn/oss/time-picker@1.0.0/element.js"></script>

<dx-time name="at" value="09:30" min="09:00" max="17:00" step="30"
         zones="Europe/London America/New_York"></dx-time>

Attributes: name, value, min, max, step, locale, hour-cycle, zones, placeholder, label, seconds. It exposes .value and .picker, and fires change with event.detail.value.

A plain page, from the CDN

<link rel="stylesheet" href="https://devix.pk/cdn/oss/time-picker@1.0.0/time-picker.min.css">
<script src="https://devix.pk/cdn/oss/time-picker@1.0.0/time-picker.min.js"></script>
<script>
  DevixTimePicker.createTimePicker(document.querySelector('#at'), { step: 30 });
</script>

jQuery

For a page that already calls $.timepicker(), there is an adapter so you can move one field at a time rather than rewriting the page:

<script src="https://devix.pk/cdn/oss/time-picker@1.0.0/jquery.js"></script>
<script>
  $('#at').timepicker({ minTime: '9:00am', maxTime: '5:00pm', step: 30 });
</script>

minTime, maxTime and disableTimeRanges are mapped to min, max and disabled; every Devix option works alongside them. $('#at').timepicker('destroy' | 'show' | 'hide') behave as before.

Blade, Livewire and Alpine

The picker drives a real <input> and fires input and change on it, so nothing needs wiring:

<input id="at" name="at" value="{{ old('at', $booking->at?->format('H:i')) }}" wire:model.blur="at">
<script type="module">
  import { createTimePicker } from '/cdn/oss/time-picker@1.0.0/index.js';
  createTimePicker(document.querySelector('#at'), { min: '09:00', max: '17:00', step: 30 });
</script>

wire:model.blur, x-model, FormData and $request->input('at') all see "14:30", which is what H:i and a time column want.

Server rendering

Nothing touches window at import time, so importing the package on a server is safe in Next.js, Nuxt, SvelteKit and Astro.

Updated 15 Sep 2026