Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Flatpickr Laravel Package

savannabits/filament-flatpickr

Filament Flatpickr swaps Filament's default date picker for the Flatpickr library, adding date ranges, time selection, locales, and flexible formatting to form fields and filters. Simple to install and configure per field.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and verify before you commit

composer require savannabits/filament-flatpickr

Two diligence items before this lands in main:

  1. Provenance check. The Composer name is savannabits/filament-flatpickr but the linked repo is coolsam/flatpickr. Confirm these are the same artifact, who holds publish rights, and whether this is a fork chain. Do this in writing; don't skip it because the install "worked."
  2. Pin the version. composer require savannabits/filament-flatpickr:^X.Y with an exact constraint (no * or floating ranges). This package is a single-maintainer wrapper around Flatpickr and will lag Filament majors.

Filament v3+ auto-discovers the service provider. If the picker renders as a plain text input with no calendar, assets didn't register — run:

php artisan filament:assets
php artisan vendor:publish --tag="filament-flatpickr-config"   # only if you need to override defaults

Then hard-refresh the browser. Do this in a real Vite production build in staging, not just php artisan serve. The single most common "it works on my machine" failure is asset registration succeeding under the dev server and failing under npm run build.

Where to look first

  • vendor/savannabits/filament-flatpickr/src/Forms/Components/ — the field class and its fluent methods. This is a small file; read it in full before writing code against it. Method names differ between versions (->range() vs ->mode('range'), ->format() vs ->dateFormat()), so trust the source over the README.
  • config/filament-flatpickr.php (after publishing) — global defaults like locale and format.
  • The published JS/CSS under public/ — confirms the asset pipeline actually shipped the Flatpickr bundle.

First use case: pick the one the native picker is bad at

Don't replace every DateTimePicker in the app. Pick a single field where Filament's stock picker is genuinely awkward — a date-range filter on a reporting dashboard, or a booking window with an event start/end on an internal admin form — and prove the whole matrix there (create, edit-existing hydration, validation re-render, non-UTC timezone). That's your spike exit criterion.

Implementation Patterns

Basic field usage

use Savannabits\FilamentFlatpickr\Forms\Components\Flatpickr;
// verify exact namespace against src/ — it may differ by version

Flatpickr::make('published_at')
    ->label('Publish at')
    ->dateFormat('Y-m-d H:i')      // maps to Flatpickr's dateFormat option
    ->time_24hr()
    ->required(),

Range selection with an array state

Range mode dehydrates to a two-element array, which changes the validation shape you write:

Flatpickr::make('stay_window')
    ->label('Check-in → Check-out')
    ->mode('range')                 // or ->range() if the version aliases it
    ->dateFormat('Y-m-d')
    ->rules(['required', 'array', 'size:2'])
    ->dehydrateStateUsing(fn ($state) => $state),  // keep as array; map in the model

Store the array in a JSON column or split it in mutateFormDataBeforeSave. Don't assume a scalar state.

Time-only and multiple-date

Flatpickr::make('shift_start')->mode('time')->dateFormat('H:i')
Flatpickr::make('campaign_dates')->mode('multiple')->dateFormat('Y-m-d')

Friendly display vs. storage format

altInput is the reason many teams reach for this package: the operator sees 12 March 2026, you persist 2026-03-12. Configure both formats explicitly so there's no ambiguity about which string hits the database:

Flatpickr::make('starts_at')
    ->dateFormat('Y-m-d')          // storage
    ->altFormat('d F Y')           // display
    ->altInput()                   // enable friendly display

Reactive fields and dependent state

The field is a normal Filament field, so ->live() and ->afterStateUpdated() work as you'd expect. Use them for cascades like "shift end must be after shift start":

Flatpickr::make('starts_at')->live()->afterStateUpdated(fn ($state, Set $set) => $set('ends_at', $state)),
Flatpickr::make('ends_at')->minDate(fn (Get $get) => $get('starts_at')),

Validation and constraints via the fluent API

->required(), ->minDate(), ->maxDate(), and ->disabledDates() all map to Flatpickr options — but they are client-side hints only. Always pair disabledDates / minDate / maxDate with a matching server-side ->rules(['after_or_equal:...']). The Flatpickr UI is not a security boundary; an operator can type a date directly into the text input.

Repeaters and Builders

Wrap each row's field so Livewire can key it uniquely and re-mount the widget correctly. The picker is imperative JS under an Alpine component; without stable keys you get pickers attaching to the wrong row after add/remove/reorder.

Filters and dashboard surfaces

Use the field inside table filter schemas for consistency, but be aware table filters re-render on every keystroke with wire:model.live. Test the range filter against rapid state changes — that's where you'll see the imperative/declarative mismatch surface.

Wrap it, always

namespace App\Filament\Fields;

class DateRange extends \Savannabits\FilamentFlatpickr\Forms\Components\Flatpickr
{
    // defaults, locale, format
}

Every schema entry uses App\Filament\Fields\DateRange::make(...). Reverting to native Filament\Forms\Components\DatePicker becomes a one-line change in one class instead of a refactor.

Gotchas and Tips

Assets fail silently, in production only. The picker "degrades" to a bare text input when Flatpickr's JS fails to load — no error, just ugly UX that operators won't report. Verify in the built bundle: npm run build, php artisan filament:assets, load in staging with DevTools Network pinned to the Flatpickr chunk.

Livewire state loss after validation. The classic failure: submit an invalid form, Filament re-renders, and the picker shows empty even though the state is still there — or worse, the picker shows a date the model doesn't have. Reproduce deliberately (create → submit invalid → correct → submit) and check what's in the wire:model payload before blaming the package. Alpine initialization order is the usual culprit; a re-init via x-init on the wrapper often fixes it.

Timezone drift writes dates one day off. Server TZ vs. browser TZ vs. the model's cast. The UI looks right and the DB is wrong — standard QA won't catch it. Test the field set to America/Los_Angeles and Asia/Tokyo against a UTC server. Store UTC, convert on display, and document the rule once for the panel rather than per-field.

Range state is an array, not a string. Validation size:2, cast to JSON, and any mutateFormDataBeforeSave must account for it. If you reuse your scalar after_or_equal rule on a range value you'll get confusing "Invalid date" errors at save time.

disabled, readonly, dehydrated(false) all behave differently. Test each explicitly — the JS init for Flatpickr on a disabled field is a known edge case in thin wrappers. Also test inside a Repeater and on a filter.

Bus factor is one. No SLA, no third-party production validation, 102 stars, zero dependents. Before this becomes load-bearing on a revenue-relevant screen, plan to vendor or fork. The codebase is thin enough that a fork is cheaper than waiting on an upstream fix during an incident.

Filament upgrades break it. Budget half a day per Filament major to re-verify this field on its own, independent of the rest of the upgrade. Add it to the upgrade checklist as a line item, and re-verify in staging with the production asset build.

Locale and format consistency. Set locale and dateFormat once in the published config (or your wrapper class) rather than per-field. Inconsistent formats across the panel is the fastest way to produce off-by-one-day bugs, because different fields round differently at midnight boundaries.

Debugging checklist when something's off

  1. DevTools → is the Flatpickr JS loaded? If not, assets are the problem.
  2. ->state() in an afterStateUpdated — what actually landed in Livewire?
  3. Browser console for Alpine errors ($refs, x-init order).
  4. wire:key present and stable inside any Repeater/Builder row.
  5. Toggle the browser timezone in DevTools and re-test create + edit.

Extension points are limited. You can reach the underlying Flatpickr options via the fluent API and extraInputAttributes, and you can override the Blade view with ->view(). But there's no plugin hook for custom calendar behavior. If you need recurrence rules, multi-resource scheduling, or drag-and-drop rescheduling, this is the wrong tool — reach for FullCalendar or a custom build. This package is a form-field polish lever, not a scheduling platform.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core