eduardoribeirodev/filament-leaflet
A Filament package that adds interactive Leaflet maps to your admin panel. Drop a map field into forms to pick coordinates visually, search locations, and save latitude and longitude without writing custom JavaScript.
This is a Filament form-field package that wraps Leaflet: you get a map picker, a geosearch input that turns place names into coordinates, and a Coordinate cast that round-trips the stored value back into the field. v5.2.2 is a bugfix release with no breaking changes, so it is a safe in-place upgrade from 5.2.x — but you do need to re-publish/rebuild the distributed CSS (see Gotchas).
Minimal path to first value:
composer require eduardoribeirodev/filament-leaflet and run the package's install/publish command from the README so the JS/CSS assets land in public/. Leaflet is not bundled by Filament, so a missing asset publish is the #1 cause of a blank grey box.Coordinate cast and add the map field to your form schema. Store as a single JSON/string column or as separate lat/lng columns — the cast supports both.First real use case: an Address/Location resource where the user picks a point on the map and you later render it in an infolist table. Get the form + cast working before wiring up geosearch or labels — those are the two areas with the most behavioural nuance.
Look first at the field's own class and the cast class in the package source; they are small and reveal the supported fluent API (textMode(), coordinateLabelUsing(), geosearch options) faster than the README does.
Cast-driven persistence. Put the cast on the model, not in the form. The field hydrates from and dehydrates to the cast, so the same value renders correctly in forms, infolists and API payloads. If your latitude/longitude live in non-standard columns, configure the cast's column names — 5.2.2 fixed custom cast columns being ignored, so if you were previously forced into latitude/longitude you can now migrate to your own names.
Geosearch: two different return shapes. This is the subtle one. When the select is populated from client-side options the value is scalar JSON; when you supply options via a PHP callback you get GeoSearchResult objects back. Read the coordinates off the object in the callback path and json_decode in the scalar path — do not assume one shape. Prior to 5.2.2 the input could bind the last search result regardless of which option was clicked, so pin ^5.2.2 if geosearch is user-facing, and write a regression test that selects a non-last option.
Labels and textMode(true). Use textMode(true) for read-only/table contexts where you want the label instead of an interactive map, and coordinateLabelUsing(fn ($coordinate) => ...) to control what that label says. The callback is the right hook for a stored address string, avoiding a runtime lookup entirely. For reverse geocoding, cache aggressively by rounded lat/lng (Nominatim's usage policy caps you at ~1 req/s and requires a User-Agent) and return the cached string; never geocode inside the callback on every table row render.
Pane/stacking control. Leaflet renders in panes with fixed z-index ordering. If you overlay polygons, markers and tooltips, register custom panes rather than nudging z-index in CSS — 5.2.2 corrected pane stacking including tooltip panes, so remove any !important z-index hacks you added as a workaround and retest.
Testing. Livewire tests can't run Leaflet, so assert on component state: set('data.coordinates', [...]), call('save'), then assert the model attribute. For the cast, test the round trip directly ($model->refresh()->coordinates) including a null/invalid stored value — 5.2.2 improved invalid-data handling rather than throwing.
>=5.2.2 in composer.json rather than ^5.2.0.null coordinates are legitimate. The cast now tolerates invalid/missing stored data. Don't assume the hydrated value is always an object — null-check before reading lat/lng in Blade/infolist renderers, and decide deliberately whether an empty field should save null or an empty array.map.invalidateSize() on the tab/modal shown event, or the map renders half-tiled and drag interactions feel offset.coordinateLabelUsing() is the sanctioned seam — prefer it over subclassing the field or overriding views, which will not survive package updates.How can I help you explore Laravel packages today?