# Adaptive Date Input

> A date input that uses the phone's native date picker on touch devices and the segmented date field with a calendar on desktop, with one ISO value. Use this when the same form runs on phones and desktops and each should get the date control it is best at.

A shadcn/ui block from the [@tealmac](https://ui.tealmac.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/tealmac/adaptive-date-input).

- Page, with a live preview: https://shadcn.exchange/r/tealmac/adaptive-date-input
- Screenshot: https://shadcn.exchange/shot/tealmac/adaptive-date-input/light.png
- Registry item: https://ui.tealmac.com/r/adaptive-date-input.json
- Listed: 2026-11-01, source last changed 2026-11-01

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/tealmac/adaptive-date-input.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `@tealmac/date-field`, `@tealmac/date-time-core`

## Files

### `registry/patterns/adaptive-date-input/adaptive-date-input.tsx`

```tsx
"use client"

import * as React from "react"

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

export interface AdaptiveDateInputProps {
  value?: string | null
  defaultValue?: string | null
  onValueChange?: (value: string | null) => void
  min?: string
  max?: string
  forceNative?: boolean
  label?: string
  locale?: string
}

/** True on a device whose main pointer is a finger. */
function useCoarsePointer(force?: boolean) {
  const [coarse, setCoarse] = React.useState(false)
  React.useEffect(() => {
    if (force !== undefined) return
    const mq = window.matchMedia("(pointer: coarse)")
    setCoarse(mq.matches)
    const on = (e: MediaQueryListEvent) => setCoarse(e.matches)
    mq.addEventListener("change", on)
    return () => mq.removeEventListener("change", on)
  }, [force])
  return force ?? coarse
}

export function AdaptiveDateInput({
  value,
  defaultValue = null,
  onValueChange,
  min,
  max,
  forceNative,
  label = "Date",
  locale,
}: AdaptiveDateInputProps) {
  const loc = resolveLocale(locale)
  const native = useCoarsePointer(forceNative)
  const [inner, setInner] = React.useState<string | null>(defaultValue)
  const date = value === undefined ? inner : value
  function change(next: string | null) {
    if (value === undefined) setInner(next)
    onValueChange?.(next)
  }

  return native ? (
    <input
      type="date"
      aria-label={label}
      min={min}
      max={max}
      value={date ?? ""}
      onChange={(e) => change(e.target.value || null)}
      className="border-input bg-background focus-visible:border-ring focus-visible:ring-ring/50 h-11 w-full max-w-60 rounded-md border px-3 text-base shadow-xs outline-none focus-visible:ring-[3px]"
    />
  ) : (
    <DateField
      label={label}
      locale={loc}
      min={min}
      max={max}
      value={date}
      onValueChange={change}
    />
  )
}
```
