Skip to content
goodtabs UI
BrowseBrowse components

Date field

A native date input with a calendar icon and an optional hint line below it.

npx shadcn@latest add https://ui.goodtabs.co/r/date-field.json

With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/date-field

One field with a label, no hint.

With min and max

Between 1 Nov and 31 Dec 2026

Any day in November or December.

A bounded field with a hint stating the allowed range.

Range

Two fields side by side, each bounded by the other.

Usage

import { DateField } from "@/components/ui/date-field"

<Label htmlFor="due">Due date</Label>
<DateField id="due" min="2026-11-01" onChange={(e) => setDue(e.target.value)} />

Props

PropTypeDefaultDescription
hintReactNodeA line under the field, for example the allowed range. Off by default because the browser formats the value for its locale.
minstringEarliest date as YYYY-MM-DD. Native Input prop.
maxstringLatest date as YYYY-MM-DD. Native Input prop.
value, defaultValue, onChangeInput propsControlled or uncontrolled, always as a YYYY-MM-DD string.

Source

date-field/date-field.tsx
import * as React from "react";
import { CalendarIcon } from "lucide-react";
import { Input } from "@/components/ui/input";
import { cn } from "@/lib/utils";

type DateFieldProps = Omit<React.ComponentProps<typeof Input>, "type"> & {
  /** A hint under the field, for example the range or the format. The browser formats the value for its locale, so only state a format when you control it. */
  hint?: React.ReactNode;
};

/**
 * A native date input with a calendar icon and an optional mono hint line.
 * Native on purpose: it gets the platform picker, keyboard entry and screen reader support for free.
 */
export function DateField({ hint, className, id, "aria-describedby": describedBy, ...props }: DateFieldProps) {
  const generated = React.useId();
  const inputId = id ?? generated;
  const hintId = `${inputId}-hint`;

  return (
    <div data-slot="date-field" className={cn("flex w-full flex-col gap-1.5", className)}>
      <div className="relative flex items-center">
        <CalendarIcon
          className="pointer-events-none absolute left-2.5 size-4 text-muted-foreground"
          aria-hidden="true"
        />
        <Input
          type="date"
          id={inputId}
          aria-describedby={cn(hint && hintId, describedBy) || undefined}
          className="relative pl-8 tabular-nums [&::-webkit-calendar-picker-indicator]:absolute [&::-webkit-calendar-picker-indicator]:inset-y-0 [&::-webkit-calendar-picker-indicator]:left-0 [&::-webkit-calendar-picker-indicator]:m-0 [&::-webkit-calendar-picker-indicator]:w-8 [&::-webkit-calendar-picker-indicator]:cursor-pointer [&::-webkit-calendar-picker-indicator]:opacity-0 [&::-webkit-date-and-time-value]:text-left"
          {...props}
        />
      </div>
      {hint && (
        <span id={hintId} className="font-mono text-xs text-muted-foreground">
          {hint}
        </span>
      )}
    </div>
  );
}

For AI agents

Hand your agent this page, the markdown at /components/date-field/prompt.md, or the Copy AI prompt button above.

When to use

  • Due dates, birthdays, start and end dates in a form
  • Anywhere a single day is enough and the platform picker is welcome
  • Date ranges, as two fields bounded by each other

When not to use

  • Picking a range on one calendar or showing availability: build a custom calendar
  • Date and time together, which needs type="datetime-local" and its own layout
  • Anything that must look the same in every browser; native pickers differ by platform

Agent notes

  • Server component: it holds no state. It extends shadcn's Input, so value, min, max, disabled and onChange pass through
  • The value is always a YYYY-MM-DD string whatever the display locale; the browser displays it in the user's locale, so do not promise a display format in a hint
  • The native picker glyph is hidden and its hit area sits over the calendar icon, so clicking the icon still opens the picker in Chromium
  • Pair it with a Label through id; the hint is linked by aria-describedby when shown