malzariey/filament-daterangepicker-filter
Native Alpine.js date range picker and filter for Filament with day, month, and year modes, presets, optional time selection, keyboard input, localization, and modal/slide-over compatibility. Includes drop-in DateRangePicker field and DateRangeFilter.
Architecture fit
The package is a thin, presentational Filament v4 plugin: it ships two entry points (Fields\DateRangePicker and Filters\DateRangeFilter), a Blade view, and an Alpine.js + Day.js + Floating UI front end. It introduces no migrations, no queue workers, no persistence layer, and no new service provider configuration beyond optional translation publishing. That means it sits entirely inside the admin/panel tier and cannot contaminate domain code.
For a TPM this is the ideal shape for a UI dependency: high user-visible value (presets like "Today"/"Last 30 Days" remove a common source of support tickets and mis-typed date filters), near-zero architectural surface area, and a small enough code footprint that forking or replacing it is a days-not-months exercise. The filter's modifyQueryUsing(fn (Builder $query, ?Carbon $start, ?Carbon $end, $dateString) => ...) hook is the right seam — it hands us the parsed Carbon boundaries and lets our team own the SQL, which is where correctness actually matters.
The v5 rewrite (jQuery/Moment.js → Alpine/Day.js) is the correct long-term direction: it aligns with Filament's own stack, removes two legacy transitive dependencies, and fixes the modal/slide-over z-index class of bug via teleport() to <body>.
Integration feasibility
Low. composer require plus two use statements gets a working filter. Everything is opt-in fluent configuration, so a first integration can be a single DateRangeFilter::make('created_at') on one table. No infrastructure prerequisites, no credential or secret handling, no external service calls. A mid-level Laravel developer can ship a pilot in under a day; the README covers the options we are most likely to need (presets, min/max, timezone, format tokens, dual state).
Technical risk
Ranked by blast radius, not likelihood:
getSystemTimezone() returned Filament's display timezone rather than config('app.timezone'), so once anyone called FilamentTimezone::set() (the documented way to render local times over UTC storage), parsed local wall-clock values were bound against UTC columns and rows early in the local day were silently filed under the previous day. A filter that returns wrong rows without erroring is the worst failure mode we can inherit. The fix and its five accompanying tests are reassuring, but the episode tells us the timezone path is under-exercised, and confirms we need our own regression coverage rather than trusting the package's.displayFormat() → format() with Carbon tokens, jQuery removal) shows upgrades are not free.Key questions
composer.json, and does that constraint match our panel's pinned version? Is there a stated support policy for the previous Filament major?TimezoneSeparationTest and the timezone/matching suites — specifically for the filter's apply() against joined/relationship columns, null state, and partial input?FilamentTimezone::set()? If yes, this is a mandatory 5.0.7+ pin and a mandatory regression test.Stack fit
Assumes Laravel 11/12 + Filament v4 + Livewire 3/4 with Alpine already bundled by Filament — in which case Day.js and Floating UI arrive as the only meaningful additions. The package's philosophy (fluent field/filter API, Filament-native styling, PHP Carbon tokens auto-converted to JS display formats) matches how our engineers already write Filament code, so there is no new idiom to learn.
Migration path
displayFormat, setTimePickerOption, setAutoApplyOption, and the rest) and standardize on format() with Carbon tokens.composer update. The breaking changes are mechanical but wide: every displayFormat('DD/MM/YYYY') call site must become format('d/m/Y'), and any custom JS that leaned on jQuery/Moment event payloads must be re-pointed at the new apply.daterangepicker / cancel / show / hide DOM events. Enumerate call sites first (grep for the deprecated methods) to size it.Compatibility
FilamentTimezone::set() scenario if our panels render local times over UTC storage — this is the one compatibility axis with silent failure.php artisan filament:assets / our cache-warming step, and that the teleported dropdown behaves inside our deepest nested modal.Sequencing
DateRangeFilter and one DateRangePicker field; exercise day, month, and year modes; confirm asset loading and modal teleport in our real theme.FilamentTimezone, applies the filter, and asserts exact row sets. This is non-negotiable and is the gate on everything after.App\Filament\Filters\AppDateRangeFilter) that centralizes our defaults — format('d/m/Y'), firstDayOfWeek(1), a shared ranges() preset set, and withIndicator(). Every call site uses the wrapper, never the vendor class.The wrapper class is the single most valuable step here: it makes the exit cost of a swap roughly "edit one file," which is the leverage we want from a 178-star dependency.
Maintenance
Near-zero steady state: no migrations to run, no schema to version, no background processes, no storage growth. The recurring cost is dependency hygiene — reading patch releases before merging them. Given the churn pattern (three behavior-affecting patches in the recent window), budget ~30 minutes per release review and a hard rule that timezone- and preset-related patches get a staging verification, not an auto-merge. Translation files are publishable; if we support non-English admin users, publishing and owning those translations is a small ongoing commitment (Day.js locale coverage determines what we can offer).
Support
One maintainer, MIT, issues on GitHub, no commercial SLA, no paid tier. That is acceptable because the wrapper class bounds our exposure and the package is small enough to patch ourselves if needed. Escalate to a fork only if a defect blocks a release; a fork of this scope is maintainable by one of our engineers part-time. Internally, name an owner (not a team) so upgrade decisions have a decision-maker.
Scaling
Client-side only, so server cost is effectively zero — the filter compiles to a whereBetween on an indexed column. Two scaling considerations:
whereBetween on created_at needs an index, and modifyQueryUsing must not be used to build non-sargable predicates. Timezone conversion happens before binding, so the column stays index-usable — good.Failure modes
app.timezone or FilamentTimezone::set() triggers a filter regression run.teleport(false) or drops()/opens() are the knobs). Caught in QA, not production.Ramp-up
Under a day for a developer already fluent in Filament: the API is self-documenting, the README covers the configuration space, and the two usage patterns (field, filter) are single lines. The genuine learning curve is not the package but the conventions we impose on it — Carbon-token formatting instead of Day.js tokens, the shared preset set, the timezone contract, and "always use the wrapper." Those belong in a two-page internal note and a code review checklist. Front-end engineers need roughly an hour on the custom DOM events if we wire date ranges into dashboard widgets or charts; that is the only place where the package leaks past the Filament boundary, and it is worth documenting deliberately.
How can I help you explore Laravel packages today?