tuxones/filament-js-money-field
A Filament form field for Laravel apps that simplifies monetary value input and formatting using JavaScript Money, with locale-aware display and validation for clean money handling in admin panels.
tuxones/filament-js-money-field is a pre-1.0 Filament form field that wraps a JavaScript money/currency formatter so users type and see masked, locale-aware amounts while your app stores a clean value. v0.1.7 is a small bug-fix release, not a feature drop — nothing to re-learn, but worth bumping if you build forms without an Eloquent record.
Minimal path in:
composer require tuxones/filament-js-money-field (confirm the constraint against your Filament major — the package tracks Filament v3 / Livewire 3).php artisan filament:assets) so the field's JS bundle is served. This is the single most common "nothing renders" cause.README.md first for the exact option names, then skim src/ — the field class and its Blade view plus the compiled JS are the whole package.use Tuxones\FilamentJsMoneyField\Forms\Components\JsMoneyField;
JsMoneyField::make('price')
->currency('EUR')
->locale('de_DE') // check README for exact method names
->required();
Pair it with a model cast on day one ('price' => 'decimal:2') so you never touch floats.
Store minor units, display major units. The cleanest pattern is integer cents in the database plus a mask on the field. Hydrate and dehydrate around the field rather than in the model:
JsMoneyField::make('price')
->formatStateUsing(fn ($state) => $state === null ? null : $state / 100)
->dehydrateStateUsing(fn ($state) => $state === null ? null : (int) round($state * 100));
If you prefer decimal columns, skip the conversion and let decimal:2 do the work — but pick one and apply it everywhere.
Validation runs on the raw state. Add ->rules(['numeric', 'min:0']) and, if the widget submits formatted strings, normalise in dehydrateStateUsing before validation or use a ->rule() closure that strips separators. Testing this once with a German locale (1.234,56) saves a support ticket later.
Same field across contexts. It works in resource forms, relation managers, repeaters and standalone Filament::form() Livewire components — the latter two are exactly where the v0.1.7 null-model fix matters. Repeater rows are cloned client-side, so verify the mask re-binds on the second and third row (see Gotchas).
Read-only display. The field is form-only; for tables use Filament's built-in TextColumn::money() so formatting stays consistent with the rest of the admin panel. Don't try to reuse the form component in a table.
Integration with totals. For invoice-style forms that sum lines live, bind the field to a wire:model path and recompute in updated() hooks. Keep the money library's formatting purely presentational — the authoritative total should be computed server-side in PHP to avoid drift between JS rounding and bcmath/integer maths.
getColumnType() previously assumed a bound Eloquent model. If you saw Call to a member function ... on null when rendering the field inside a standalone form, a repeater before hydration, or a form not backed by a record, this release fixes it. Cheap upgrade, no migration steps.0.1.x; per semver, minor bumps may break. Pin to ^0.1.7 and read the changelog on each bump rather than floating *.composer update, re-run php artisan filament:assets (and clear views/config) in CI and locally. A cached old bundle is the usual suspect when formatting behaviour doesn't match the code you just wrote.Livewire.hook('morph.updated') for you, that's the extension point to subclass or patch.locale() on the field and app()->getLocale() on the server can disagree, producing a value the user didn't intend. Set the field locale explicitly rather than relying on the app default.getColumnType() override. The field maps itself to a decimal-ish column type for Filament's schema tooling. If you generate migrations from the form, verify the emitted type matches your actual column — and if you store integer cents, override getColumnType() in a subclass.->dd() on the dehydrated state tells you what the server actually receives; browser console errors from the underlying money library tell you what the client did with it. Check both before assuming a package bug — most issues are state conversion, not formatting.setUp(), getColumnType() or the default hydration/dehydration; publish the Blade view (php artisan vendor:publish --tag="filament-js-money-field-views") if you need markup changes rather than forking.How can I help you explore Laravel packages today?