DEV Community

Cover image for Wirepicker: a date and date-range picker for Laravel and Livewire
Eduardo Lázaro
Eduardo Lázaro

Posted on

Wirepicker: a date and date-range picker for Laravel and Livewire

Nobody uses <input type="date"> in a real app. It shows the browser's format instead of the page's, it cannot be styled and it has no ranges.

Wirepicker replaces it with one Blade component: single dates and ranges, shortcuts, keyboard, every language, no runtime dependencies. It works with Livewire, with Alpine or with a plain form.

Install

It is a regular Laravel package. Require it with Composer and publish the assets:

composer require edulazaro/wirepicker
php artisan vendor:publish --tag=wirepicker-assets
Enter fullscreen mode Exit fullscreen mode

Then load the CSS and the JS in your layout:

<link rel="stylesheet" href="{{ asset('vendor/wirepicker/css/wirepicker.css') }}">
<script src="{{ asset('vendor/wirepicker/js/wirepicker.js') }}" defer></script>
Enter fullscreen mode Exit fullscreen mode

Or with Vite, straight from vendor:

@import "../../vendor/edulazaro/wirepicker/resources/css/wirepicker.css";
Enter fullscreen mode Exit fullscreen mode
import '../../vendor/edulazaro/wirepicker/resources/js/wirepicker.js';
Enter fullscreen mode Exit fullscreen mode

One date

Put the component where the input would go and bind it like any field:

<x-wirepicker wire:model="startsOn" />
Enter fullscreen mode Exit fullscreen mode

The property gets a Y-m-d string, or '' when empty.

A range

Add range and bind it to an array with a start and an end:

<x-wirepicker range wire:model.live="period" />
Enter fullscreen mode Exit fullscreen mode
public array $period = ['start' => '', 'end' => ''];
Enter fullscreen mode Exit fullscreen mode

Click both ends in any order. With shortcuts:

<x-wirepicker range wire:model.live="period" :presets="['this-month', 'next-month', 'this-year']" />
Enter fullscreen mode Exit fullscreen mode

Available: today, this-week, next-7-days, this-month, next-month, last-month, this-year.

Without Livewire

Outside Livewire, give it a name and an optional value, and it posts like any other field:

<x-wirepicker name="starts_on" value="2026-10-08" />
<x-wirepicker range name="period" :value="['start' => '2026-10-01', 'end' => '2026-10-31']" />
Enter fullscreen mode Exit fullscreen mode

A range posts period[start] and period[end]. For Alpine, listen to wirepicker:change:

<div x-data="{ date: null }" x-on:wirepicker:change="date = $event.detail.value">
    <x-wirepicker />
</div>
Enter fullscreen mode Exit fullscreen mode

Options

Everything is a prop on the component. These are all of them:

Prop Default
range false Two dates instead of one
presets [] Shortcuts, 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 Language, format and first day of the week
week-start from the locale 0 Sunday to 6 Saturday
id none For <label for>

Languages

Months, weekdays, date format and first day of the week come from Intl: es shows 08/10/2026 starting on Monday, en-US shows 10/08/2026 starting on Sunday. No locale files. The few words of its own ship in English and Spanish (vendor:publish --tag=wirepicker-lang to add more).

Keyboard

Arrows move a day or a week, Page Up and Page Down a month, Enter picks, Escape closes.

How it works with Livewire

The point of the package is that it keeps its value through Livewire's requests, which is where most date pickers break. Three decisions make that work:

  • wire:model goes on hidden fields inside the component. The JS writes them and fires input and change, so there is no glue to write and nothing to initialise: pickers added later just work.
  • Those fields are <input type="text" hidden>, not type="hidden". On a hidden input the value is the attribute, and Livewire's morph removes it after every request.
  • The calendar is drawn on <body>, so tables, cards and modals cannot clip it, and it opens above wiremodal modals.

Themes

Same attribute as the rest of the wire* family:

<html data-wire-theme="studio">
Enter fullscreen mode Exit fullscreen mode

11 themes, dark mode with data-wire-theme-mode="dark" or .dark. For your own look, override the --wire-* variables (whole family) or --wp-* (just the picker):

:root {
    --wp-trigger-radius: 0.5rem;
    --wp-day-size: 34px;
}
Enter fullscreen mode Exit fullscreen mode

Links

👉 Source: https://github.com/edulazaro/wirepicker
👉 Packagist: https://packagist.org/packages/edulazaro/wirepicker
👉 Documentation: https://edulazaro.com/portfolio/wirepicker

Top comments (2)

Collapse
 
beusebiu profile image
Eusebiu Balan •

Returning a plain Y-m-d string is the right call. A Date object ends up a day off for someone the moment it goes through UTC.

And week start from the locale, nice. A Sunday "this-week" looks broken to most of Europe.

Collapse
 
edulazaro profile image
Eduardo Lázaro •

Thanks! Both were on purpose, as dates stay plain strings so no time zone can move them, and the week start comes from Intl, so nobody has to configure it.