Skip to main content
WireKit
Copy for LLM

Date Picker

The <x-wirekit::date-picker> component is a styled wrapper around the native <input type="date">. It uses the browser's built-in calendar popup for zero dependencies and automatic localization to the user's OS locale.

Usage

Basic Date Picker

Width

Like all WireKit form components, the date picker fills its container (w-full). Date fields are typically narrow — constrain the width via the parent element:

Date picker in a 12rem container
<div class="max-w-[12rem]">
    <x-wirekit::date-picker label="Start date" name="start_date" />
</div>

See Input — Width for more layout examples (grid columns, mixed widths).

Date Range

Add the range flag to render a linked start + end pair. Each field is a native date input, and the two are reactively linked — the end can't be set before the start, and the start can't be set after the end. They submit as name[start] and name[end]. Pass an initial range as an array or a YYYY-MM-DD/YYYY-MM-DD string.

Date range

Range Constraints

With Min and Max

Must be in 2024

Error State

Date picker with an error message

Please enter a valid date

Why Native?

The native <input type="date"> gives us:

  • Zero dependencies — no JavaScript calendar library to ship
  • Automatic localization — calendar renders in the user's OS language and date format
  • Full accessibility — arrow keys, PageUp/PageDown for months, Home/End for week boundaries, all work out of the box
  • Mobile keyboards — native date wheel on iOS, spinner on Android
  • Browser-native parsing — handles timezone and format edge cases

The value is always submitted in YYYY-MM-DD format regardless of the display format.

Optimistic UI

Pass the name of the Livewire method the picker should call and the date lands immediately, then confirms or undoes itself when the server answers:

<x-wirekit::date-picker
    name="due"
    label="Due date"
    :value="$due"
    optimistic="saveDue"
/>

Load wirekit-optimistic.js alongside whichever bundle you already use — below it, in your layout:

@wirekitScripts
<script src="{{ asset('vendor/wirekit/wirekit-optimistic.js') }}"></script>

Try it

The demo below runs the real path: the change shows immediately, the outline says it is provisional, and the server's answer either confirms it silently or takes it back.

Optimistic date — accepted, refused, and a slow answer

The <livewire:demos.…> wrapper above exists only on this site — it supplies the demo methods so the page can show a real round trip. The block under it is what you write.

The prop takes a method name rather than true because the component cannot know the action otherwise: server actions reach a WireKit component through the attribute bag, so it never sees your wire:change. optimistic replaces wire:model.live here rather than joining it — the method you name is what changes the value on the server; pass the current one with :value so the picker knows where it started.

range mode is not covered. A range is two values, so an undo would have to answer what it restores when only one end moved — and the two inputs constrain each other, so rolling one back can leave the other holding a bound that no longer applies. Passing both props gives you the plain range picker rather than a layer applied to half of it.

What a screen reader hears Picking a date announces once, hedged — "Saving" — so the new value is audible as provisional. Confirmation is silent: what was announced is what happened. Only a deviation speaks a second time, which is what makes an undo recognizable as an undo.

Where the picker shows a validation message, the undo stays silent and leaves that message to speak: it tells you what to do, "could not save" does not.

An aborted request announces nothing at all — nothing was refused.

Focus stays exactly where you put it. An undo arrives on the server's schedule, and moving focus then would take you out of your place for a reason you could not predict.

Props

Prop Type Default Description
name string|null null Form field name (range mode submits name[start] / name[end])
id string|null auto-generated Element id
value string|array|null null Initial date YYYY-MM-DD; in range mode an array ['start' => .., 'end' => ..] or a YYYY-MM-DD/YYYY-MM-DD string
optimistic string|null null Livewire method to call, showing the new date before the server confirms it. Single-date mode only; ignored with range or disabled. See Optimistic UI.
optimisticArgs array [] Extra arguments appended to the optimistic action call, after the new value — the row this control belongs to.
range bool false When true, renders a linked start + end date pair
min string|null null Earliest selectable date (YYYY-MM-DD)
max string|null null Latest selectable date (YYYY-MM-DD)
size string 'md' 'sm', 'md', 'lg'
disabled bool false Disabled state
required bool false Required field
placeholder string|null null Placeholder (browser-dependent)
hint string|null null Helper text below input
error string|null null Error message (also reads from $errors)
scope string|null null Scoped personalization key

Accessibility

  • Native <input type="date"> — full browser a11y and keyboard support
  • aria-invalid="true" and aria-describedby on error
  • aria-required="true" when required
  • Focus ring and border color tokens adapt to light/dark mode automatically

Keyboard Interaction

Key Action
Tab Move focus into the date input
Type characters matching YYYY-MM-DD (or the configured format) Set the value directly
ArrowUp / ArrowDown (in supporting browsers) Step the focused field by ±1

Pitfalls

  • Don't pass a Carbon\Carbon instance directly. The underlying <input type="date"> expects an ISO string. Use ->format('Y-m-d') before binding, or rely on Livewire's automatic Carbon ↔ string casting.
  • Don't disable the dropdown. deliberately ships an HTML5 native picker for accessibility — disabling it sacrifices keyboard-only date entry that screen-reader users depend on.

Design Tokens

Token Used for
--text-wk-xs / --text-wk-sm / --text-wk-md / --text-wk-lg Font size per size prop
--color-wk-text Selected date text
--color-wk-text-muted Calendar icon + hint
--color-wk-text-placeholder Empty-state text
--color-wk-bg-input Input background
--color-wk-accent Selected date highlight
--color-wk-border-strong Default border
--color-wk-border-error Error-state border
--color-wk-danger-text Error message
--color-wk-ring Focus ring
--ring-wk-width Focus ring width
--border-wk-width Border width
--radius-wk-md Border radius
--size-wk-sm / --size-wk-md / --size-wk-lg Input height per size prop
--padding-wk-x-md / --padding-wk-y-xs Input padding
--opacity-wk-disabled Disabled visual weight
--transition-wk-duration Hover / focus transition

Config Defaults

The defaults live in config/wirekit.php under components.date-picker. Override them globally:

'components' => [
    'date-picker' => ['size' => 'md', 'format' => 'Y-m-d'],
],

Further Reading

Was this page helpful?

Thanks — that helps.

Voting requires cookies or local storage. What we store