chengkangzai / filament-mui-date-picker
A Filament form field that replicates MUI's DatePicker with full Livewire integration
Package info
github.com/chengkangzai/filament-mui-date-picker
Language:JavaScript
pkg:composer/chengkangzai/filament-mui-date-picker
Fund package maintenance!
Requires
- php: ^8.2
- filament/filament: ^4.0 || ^5.0
- filament/forms: ^4.0 || ^5.0
- spatie/laravel-package-tools: ^1.15.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.0
- nunomaduro/collision: ^8.0
- orchestra/testbench: ^9.0 || ^10.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- pestphp/pest-plugin-livewire: ^4.0
- rector/rector: ^2.0
- spatie/laravel-ray: ^1.26
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-15 12:59:19 UTC
README
A Filament form field powered by MUI X Date Pickers and React. It renders a fully-featured Material UI date picker inside your Filament panels with seamless Livewire two-way binding, dark mode support, and built-in localization for 40+ languages.
Features
- Material UI date picker rendered via React, embedded inside Filament forms
- Full two-way data binding with Livewire through Alpine.js
- Three picker variants: desktop (popover), mobile (dialog), and static (always-visible)
- Configurable picker views (year, month, day) and opening view
- Min/max date constraints with automatic Laravel validation rules
- Disable specific dates, past dates, or future dates
- 40+ built-in MUI locale translations plus PHP lang file overrides
- Clearable input with optional "Today" button
- Landscape and portrait orientations
- Follows Filament's dark mode and primary color automatically
- Supports
readOnly,disabled,required,placeholder, and all standard Filament field features - Display format and storage format can be configured independently
Requirements
- PHP 8.2+
- Laravel 11 or 12
- Filament 4.x or 5.x
- Node.js (for building assets during development only)
Installation
Install the package via Composer:
composer require chengkangzai/filament-mui-date-picker
Register the plugin in your Filament panel provider:
use Cck\FilamentMuiDatePicker\MuiDatePickerPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ MuiDatePickerPlugin::make(), ]); }
Optionally, publish the configuration file:
php artisan vendor:publish --tag="mui-date-picker-config"
To publish and customize translation files:
php artisan vendor:publish --tag="mui-date-picker-translations"
Basic Usage
Add the MuiDatePicker field to any Filament form schema:
use Cck\FilamentMuiDatePicker\Forms\Components\MuiDatePicker; MuiDatePicker::make('date') ->label('Date')
The field stores dates in Y-m-d format by default and displays them as MM/DD/YYYY.
Validation runs on the server: required, date, minDate() / maxDate(), disabledDates(), disablePast() and disableFuture() are all enforced as Laravel rules, so a value posted directly to Livewire cannot bypass them.
Configuration Options
Display Format and Storage Format
The display format controls how the date appears in the input field (uses Day.js tokens). The storage format controls how the date is stored in your database (uses PHP date format tokens).
MuiDatePicker::make('date') ->displayFormat('DD/MM/YYYY') // Day.js format shown to the user ->format('Y-m-d') // PHP format stored in the database
| Method | Default | Format Type |
|---|---|---|
displayFormat() |
MM/DD/YYYY |
Day.js |
format() |
Y-m-d |
PHP |
The storage format is applied when the form is loaded and saved, like Filament's native DatePicker: a stored value is parsed with format() when the form is filled, and the selected date is formatted with format() when you call $form->getState(). While the form is open, the Livewire state ($this->data['date'], $wire.data.date) is always an ISO Y-m-d date, whatever format() is. $get('date') in a closure returns the value in format(), as with the native picker.
Model datetime casts (Carbon instances) and datetime strings with an offset are converted to the field's timezone() before the date is taken. If format() contains a time (e.g. Y-m-d H:i:s), the saved value is midnight in the field's timezone, converted to the app timezone.
Min and Max Dates
Constrain the selectable date range. These methods automatically add before_or_equal and after_or_equal Laravel validation rules.
Limits accept an ISO date, a date in your format(), a Carbon / DateTimeInterface instance, a closure, or a relative string such as today or tomorrow (resolved on the server in the field's timezone()). They are resolved to a Y-m-d date once, on the server, and the same date is used for the picker and for validation.
use Carbon\Carbon; use Filament\Schemas\Components\Utilities\Get; MuiDatePicker::make('date') ->minDate('2024-01-01') ->maxDate('2024-12-31') // Or with Carbon instances: MuiDatePicker::make('date') ->minDate(now()->startOfMonth()) ->maxDate(now()->endOfMonth()) // Relative strings: MuiDatePicker::make('date') ->minDate('tomorrow') // Depending on another field. The picker is updated on every Livewire render, so make the // field it depends on live(): MuiDatePicker::make('start_date')->live() MuiDatePicker::make('end_date') ->minDate(fn (Get $get) => $get('start_date'))
Closures for minDate(), maxDate(), disabledDates(), disabled(), readOnly() and the rest of the picker's options are re-evaluated on every Livewire render, and the new configuration is passed to the open picker without remounting it.
Disabled Dates
Pass an array (or a closure returning one) of specific dates that cannot be selected. They are also rejected by a server-side not_in rule, with the message "The selected date is not available." (mui-date-picker::validation.disabled_date, or override it with validationMessages(['not_in' => '...'])).
MuiDatePicker::make('date') ->disabledDates([ '2024-12-25', '2024-12-31', Carbon::parse('2024-01-01'), ])
Disable Future / Disable Past
Quickly prevent selection of future or past dates. "Today" is computed on the server in the field's timezone() and passed to the picker as its min/max date, so the browser's clock and timezone cannot disagree with validation; the matching after_or_equal / before_or_equal rule is enforced on the server. Combined with minDate() / maxDate(), the stricter limit wins.
MuiDatePicker::make('date') ->disableFuture() // Only past dates and today MuiDatePicker::make('date') ->disablePast() // Only today and future dates
Picker Variant
Choose how the date picker is presented. The default is desktop.
// Desktop: opens as a popover anchored to the input MuiDatePicker::make('date')->desktop() // Mobile: opens as a full-screen dialog MuiDatePicker::make('date')->mobile() // Static: renders the calendar inline, always visible MuiDatePicker::make('date')->staticMode() // Or use the generic method: MuiDatePicker::make('date')->variant('mobile')
Picker Views and Opening View
Control which views (year, month, day) are available and which one the picker opens to:
// Only allow year and month selection MuiDatePicker::make('date') ->pickerViews(['year', 'month']) ->openTo('year') // Open directly to year selection, then drill down MuiDatePicker::make('date') ->pickerViews(['year', 'month', 'day']) ->openTo('year')
Clearable
Allow the user to clear the selected date:
MuiDatePicker::make('date') ->clearable()
Today Button
Show a "Today" button in the picker toolbar for quick selection:
MuiDatePicker::make('date') ->showTodayButton()
Toolbar
Show or hide the picker toolbar:
MuiDatePicker::make('date') ->showToolbar()
Close on Date Selection
By default, the picker closes when a date is selected. You can keep it open:
MuiDatePicker::make('date') ->closeOnDateSelection(false)
Orientation
Control the layout orientation of the picker:
MuiDatePicker::make('date')->landscape() MuiDatePicker::make('date')->portrait() // Or use the generic method: MuiDatePicker::make('date')->orientation('landscape')
Reduce Animations
Disable picker animations for performance or accessibility:
MuiDatePicker::make('date') ->reduceAnimations()
Disable Today Highlight
Remove the visual highlight on today's date in the calendar:
MuiDatePicker::make('date') ->disableHighlightToday()
Locale
Set the locale for date formatting and UI text. Falls back to config('app.locale'):
MuiDatePicker::make('date') ->locale('fr')
See the Localization section for full details.
Locale Text Overrides
Override specific UI text strings on a per-field basis:
MuiDatePicker::make('date') ->localeText([ 'cancelButtonLabel' => 'Dismiss', 'clearButtonLabel' => 'Reset', 'todayButtonLabel' => 'Jump to today', ])
Timezone
Set the timezone for date handling. Falls back to config('app.timezone'):
MuiDatePicker::make('date') ->timezone('America/New_York')
First Day of Week
Override the first day of the week (0 = Sunday, 1 = Monday, etc.):
MuiDatePicker::make('date') ->firstDayOfWeek(1) // Monday
Default Focused Date
Set which date the calendar focuses on when opened without a value:
MuiDatePicker::make('date') ->defaultFocusedDate('2024-06-15') // Or with Carbon: MuiDatePicker::make('date') ->defaultFocusedDate(now()->addMonth())
Placeholder
Set placeholder text shown when no date is selected:
MuiDatePicker::make('date') ->placeholder('Select a date...')
Read Only and Disabled
// Read-only: the user can see the value but cannot change it MuiDatePicker::make('date') ->readOnly() // Disabled: the field is grayed out and non-interactive MuiDatePicker::make('date') ->disabled()
Localization / i18n
The date picker supports 40+ languages through three layers of localization that merge together. Each layer overrides the one below it.
Layer 1: Built-in MUI Locale Translations (JavaScript)
The following 40 locales ship with built-in MUI translations for all picker UI text (button labels, toolbar titles, accessibility strings, etc.). When you set ->locale('fr'), MUI's French translations are loaded automatically with no extra configuration:
| Locale Code | Language | Locale Code | Language |
|---|---|---|---|
be |
Belarusian | ja |
Japanese |
bg |
Bulgarian | kk |
Kazakh |
bn / bn_BD |
Bengali | ko |
Korean |
ca |
Catalan | mk |
Macedonian |
cs |
Czech | nb |
Norwegian Bokmal |
da |
Danish | nl |
Dutch |
de |
German | nn |
Norwegian Nynorsk |
el |
Greek | pl |
Polish |
en / en_GB |
English | pt / pt_PT |
Portuguese |
es / es_MX |
Spanish | pt_BR |
Brazilian Portuguese |
eu |
Basque | ro |
Romanian |
fa |
Persian | ru |
Russian |
fi |
Finnish | sk |
Slovak |
fr / fr_CA |
French | sv |
Swedish |
he |
Hebrew | tr |
Turkish |
hr |
Croatian | uk |
Ukrainian |
hu |
Hungarian | ur |
Urdu |
is |
Icelandic | vi |
Vietnamese |
it |
Italian | zh / zh_CN / zh_HK / zh_TW |
Chinese |
Layer 2: PHP Lang Files (Publishable)
For languages not covered by MUI's built-in translations (or to customize the built-in ones), the plugin ships PHP translation files. These are loaded via Laravel's standard translation system and merged on top of Layer 1.
The plugin ships with lang files for these languages:
en-- Englishar-- Arabicms-- Malayta-- Tamilth-- Thaihi-- Hindiid-- Indonesiansw-- Swahilifil-- Filipinokm-- Khmer
To publish and customize these files:
php artisan vendor:publish --tag="mui-date-picker-translations"
This copies the lang files to lang/vendor/mui-date-picker/ in your application, where you can edit them freely.
Available Translation Keys
Each lang file returns an array with these keys:
return [ 'previousMonth' => 'Previous month', 'nextMonth' => 'Next month', 'openPreviousView' => 'Open previous view', 'openNextView' => 'Open next view', 'cancelButtonLabel' => 'Cancel', 'clearButtonLabel' => 'Clear', 'okButtonLabel' => 'OK', 'todayButtonLabel' => 'Today', 'datePickerToolbarTitle' => 'Select date', 'start' => 'Start', 'end' => 'End', 'startDate' => 'Start date', 'endDate' => 'End date', 'year' => 'Year', 'month' => 'Month', 'day' => 'Day', 'hours' => 'Hours', 'minutes' => 'Minutes', 'seconds' => 'Seconds', 'empty' => 'Empty', ];
Adding a New Language
- Publish the translations (if you have not already).
- Create a new file at
lang/vendor/mui-date-picker/{locale}/mui-date-picker.php. - Translate all the keys listed above.
- Set
->locale('{locale}')on your field or setconfig('app.locale')globally.
Layer 3: Per-Field localeText() Overrides
The highest-priority layer. Use localeText() on individual fields to override any translation string:
MuiDatePicker::make('date') ->locale('fr') ->localeText([ 'todayButtonLabel' => "Aujourd'hui", 'clearButtonLabel' => 'Effacer', ])
How the Layers Merge
When the picker renders, translations are resolved in this order:
- MUI's built-in JavaScript translations for the locale (if available)
- PHP lang file translations are merged on top (from
lang/vendor/mui-date-picker/{locale}/or the plugin's bundled files) - Per-field
localeText()values are merged last, taking highest priority
This means you can rely on MUI's built-in translations for most languages, customize individual strings through PHP lang files, and apply one-off overrides on specific fields.
Real-World Examples
Date of Birth (18+ Required)
MuiDatePicker::make('date_of_birth') ->label('Date of Birth') ->required() ->maxDate(now()->subYears(18)) ->openTo('year') ->pickerViews(['year', 'month', 'day']) ->displayFormat('DD/MM/YYYY') ->placeholder('Select your date of birth...') ->disableFuture()
Appointment Booking (Future Dates Only, No Weekends)
use Carbon\Carbon; use Carbon\CarbonPeriod; // Build a list of weekend dates for the next 6 months $weekends = collect(CarbonPeriod::create(now(), now()->addMonths(6))) ->filter(fn (Carbon $date) => $date->isWeekend()) ->map(fn (Carbon $date) => $date->format('Y-m-d')) ->values() ->all(); MuiDatePicker::make('appointment_date') ->label('Appointment Date') ->required() ->disablePast() ->disabledDates($weekends) ->clearable() ->showTodayButton() ->placeholder('Choose an available weekday...')
Simple Date Field
MuiDatePicker::make('date') ->label('Date')
Inline Static Calendar
MuiDatePicker::make('event_date') ->label('Event Date') ->staticMode() ->showTodayButton() ->clearable()
Localized Date Picker
MuiDatePicker::make('date') ->label('Tarikh') ->locale('ms') ->displayFormat('DD/MM/YYYY') ->firstDayOfWeek(1) ->clearable()
Live Fields
A picked date is written to Livewire once. On a live() field (including live(onBlur: true) and live(debounce: ...)) it is sent to the server immediately; on a regular (deferred) field it is sent with the next request, such as the form submit.
Accessibility
The visible picker input takes the field's id, so the Filament label names and focuses it. It also gets the field's required state, aria-invalid when the field has a validation error, and aria-describedby pointing at the helper text and error message (Filament 5.10+). A field with hiddenLabel() gets an aria-label instead.
Styling
The date picker automatically adapts to Filament's dark mode and uses your panel's primary color (Filament 4/5 oklch() colors are converted for MUI; an unreadable color falls back to indigo). No additional CSS configuration is needed.
If the picker's JavaScript bundle cannot be loaded within about ten seconds, the field logs a console error and shows a plain browser date input instead.
The component uses the following CSS classes if you need to target it for custom styling:
.fi-fo-mui-date-picker-- the outer wrapper.fi-fo-mui-date-picker-container-- the React mount point.fi-fo-mui-date-picker-fallback-message/.fi-fo-mui-date-picker-fallback-input-- the fallback shown if the bundle fails to load
Testing
composer test # Pest npm test # Vitest (React picker and Alpine bridge)
resources/dist is committed. After changing anything in resources/js or resources/css, run npm run build and commit the result; CI fails if the committed build is out of date.
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.