Wirepicker

Wirepicker is a date and date-range picker for Laravel that works with or without Livewire and with or without Alpine. It ships one Blade component, a small vanilla JS controller and its own CSS, with zero runtime dependencies, and shares the data-wire-theme attribute with wiremodal and wiretoast.
It exists because <input type="date"> shows the browser's format, not the page's: a Spanish page on an English browser reads mm/dd/yyyy, and the control cannot be styled.
Installation
composer require edulazaro/wirepicker php artisan vendor:publish --tag=wirepicker-assets
The publish command drops CSS and JS into public/vendor/wirepicker:
<link rel="stylesheet" href="{{ asset('vendor/wirepicker/css/wirepicker.css') }}"> <script src="{{ asset('vendor/wirepicker/js/wirepicker.js') }}" defer></script>
With Vite you can skip publishing and import straight from vendor:
/* resources/css/app.css */ @import "../../vendor/edulazaro/wirepicker/resources/css/wirepicker.css";
// resources/js/app.js import '../../vendor/edulazaro/wirepicker/resources/js/wirepicker.js';
wirepicker-lang and wirepicker-views publish the translations and the Blade view.
One date, or a range
<x-wirepicker wire:model="startsOn" />
The property holds a Y-m-d string, '' when empty. Any wire:model modifier works.
<x-wirepicker range wire:model.live="period" />
public array $period = ['start' => '', 'end' => ''];
The first click sets the start and the second the end, in either order, and the days in between light up under the pointer while the end is still open. A range can carry shortcuts beside the calendar:
<x-wirepicker range wire:model.live="period" :presets="['this-month', 'next-month', 'this-year']" />
The shortcuts are today, this-week, next-7-days, this-month, next-month, last-month and this-year. An unknown name is dropped.
Without Livewire
Give it a name and it posts like any field; a range posts period[start] and period[end]:
<x-wirepicker name="starts_on" value="2026-10-08" /> <x-wirepicker range name="period" :value="['start' => '2026-10-01', 'end' => '2026-10-31']" />
Every change also fires a bubbling wirepicker:change event on the root, which is all Alpine needs:
<div x-data="{ date: null }" x-on:wirepicker:change="date = $event.detail.value"> <x-wirepicker /> </div>
Options
| Prop | Default | |
|---|---|---|
range |
false |
Two dates instead of one |
presets |
[] |
Shortcuts beside the calendar, range only |
placeholder |
translated | Text while empty |
min, max |
none | First and last day that can be picked |
clearable |
true |
A button to empty it |
name, value |
none | For a plain form |
size |
md |
sm for toolbars |
locale |
app locale | Months, weekdays, format and first day of the week |
week-start |
from the locale | 0 Sunday to 6 Saturday |
id |
none | Set on the button, for <label for> |
Every language, almost no translations
Months, weekdays, the date format and the first day of the week come from the browser's Intl, so es reads 08/10/2026 and starts on Monday while en-US reads 10/08/2026 and starts on Sunday, with no locale files. Only the package's own few words (the placeholder, Today, Clear and the shortcuts) are translated, in English and Spanish.
How it survives Livewire
The value lives in hidden fields inside the component, and the controller writes them and fires input and change: that is the whole contract with wire:model, a form and Alpine. Two details make it hold up after every Livewire request:
- The fields are
<input type="text" hidden>, nottype="hidden". On a hidden input the value is the attribute, and a morph whose HTML carries no value empties it. - The calendar is drawn once on
<body>, so no scrolling table, card or modal around the picker can clip it, and it opens above wiremodal's modals.
Theming
Pick the theme once on <html>, shared with the rest of the family:
<html data-wire-theme="studio">
The picker has the same eleven themes as the rest of the wire* family, with dark mode through data-wire-theme-mode="dark" or a .dark ancestor. A theme set on a container instead of <html> works too: the calendar takes the picker's theme with it when it opens.
For anything else, override CSS variables: the shared --wire-* ones for the whole family, or the picker's own --wp-* ones for the field and the calendar.
built and maintained by Edu Lazaro · MIT license