Skip to content

TimeField

MD · 1950B gzip budget

A native time input matching the platform's own 12/24-hour convention.

Category
Forms & input
Budget
md, several states or a live subscription
External runtime
React / React DOM only
axe fixtureSSR rendersource scanbundle budget

Preview

Live fixture
Preview locale
9:30 AM

Try the component here. Locale-aware formatting and direction follow the selected language; example content and labels are not automatically translated. Overlay demos use interactive launchers.

Install

npx shadcn@latest add https://gear5-ui.vercel.app/r/time-field.json

Copies the source into your project. Pulls in 2 shared registry items: use-locale, locale.

New here? Set up Tailwind and import aliases first →

Source & props

Edit on GitHub ↗

Prop types and inline documentation are included below. This is repository source; the installer rewrites shared imports for your project.

"use client";

import { useId, useMemo } from "react";
import { useLocale } from "../lib/use-locale";

export interface TimeFieldProps {
  value: string; // "HH:MM", 24-hour, as <input type="time"> always uses internally
  onChange: (value: string) => void;
  label: string;
  className?: string;
}

/**
 * A native `<input type="time">`. The browser already renders it 12-hour with
 * an AM/PM picker or 24-hour, matching each user's own OS locale setting —
 * exactly what `Intl.Locale`'s `hourCycle` would tell a custom-built field to
 * do by hand, for free and for every locale the platform itself supports.
 */
export function TimeField({ value, onChange, label, className }: TimeFieldProps) {
  const id = useId();
  const { locale } = useLocale();

  const preview = useMemo(() => {
    if (!value) return "";
    const [hours, minutes] = value.split(":").map(Number);
    const date = new Date();
    date.setHours(hours, minutes, 0, 0);
    return new Intl.DateTimeFormat(locale, { timeStyle: "short" }).format(date);
  }, [value, locale]);

  return (
    <div className={className}>
      <label htmlFor={id} className="mb-1.5 block text-sm font-medium">
        {label}
      </label>
      <input
        id={id}
        type="time"
        value={value}
        onChange={(event) => onChange(event.target.value)}
        aria-describedby={preview ? `${id}-preview` : undefined}
        className="rounded-md border border-neutral-300 bg-white px-3 py-2 text-base focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 dark:border-neutral-700 dark:bg-neutral-950"
      />
      {preview && (
        <span id={`${id}-preview`} className="sr-only">
          {preview}
        </span>
      )}
    </div>
  );
}

What CI checks

Quality contract
  • Bundled, minified and gzipped against its tier budget
  • Audited by axe in the state previewed above
  • Rendered through react-dom/server with no browser globals
  • Scanned for network calls, dangerous sinks, and unguarded animation