Laravel Nova 5 · MIT
A map field that behaves like a Nova field.
FieldMap reads your coordinate column, works out what shape it is in, and draws it.
GeoJSON, "45.6495,13.7768", a [lat, lng] array or a spatial
Point — all understood without being told. Make it editable and an
administrator drops the pin themselves.
01 / The smallest real thing
Two arguments. That is the whole field.
No type(), no handing the data in yourself. Name the field, name the column,
and the resolver chain works out what it found.
use Gabrielesbaiz\NovaFieldMap\FieldMap; public function fields(NovaRequest $request): array { return [ FieldMap::make('Location', 'coordinates'), ]; }
The coordinates column can hold any of these. Nothing tells the field which:
'45.6495,13.7768'a string[45.6495, 13.7768]a positional array['lat' => 45.6495, 'lng' => 13.7768]a keyed array'{"type":"Point","coordinates":[13.7768,45.6495]}'GeoJSONnew Point(45.6495, 13.7768)a spatial value object02 / Editable
Two more calls and an administrator places the pin.
2.x forced showOnUpdate to false and shipped a text input that did nothing.
3.0 gives you a real form field: click to place, drag to adjust, search for a place, or
type the numbers.
FieldMap::make('Location', 'coordinates') ->editable() ->geocoder() ->height('400px')
03 / A whole relation
One marker per related record, clustered, clickable.
Each related record's attributes travel with its feature, so popups and per-marker styling
address them by column name. linkToResource() makes a marker click open that
record's detail page through Inertia.
public static $with = ['branches']; FieldMap::make('Branches', 'coordinates') ->relation('branches') ->popup('name') ->linkToResource() ->cluster(disableAtZoom: 12) // popup('name') is escaped. Markup needs // an explicit ->allowHtmlPopups().
04 / Decide by driving it
Every method, every option, live.
The playground has a control for all 58 public methods. Change one and three things move at
once: the chain you would paste into a resource, the field Nova would draw, and the payload
the browser actually receives — generated by the same rules as mapMeta(), so the
derived values behave exactly as they do in your app.
What Nova draws
Markers, clusters, heat, choropleth, the draw toolbar, the search box, the legend, the index column. Turn a thing on and it appears.
The chain to paste
Only the calls that differ from a default are emitted, with the right
use statements, so what you copy is what you would have written.
All sixty variables
The meta array the component receives, with the computed keys flagged — clustering forced off, the canvas threshold, a keyless tile being dropped.
05 / What changed in 3.0
A rewrite, driven by three things that were wrong.
Popups were an XSS hole
Leaflet's bindPopup() renders a string as HTML, and 2.x passed feature
properties to it untouched. Anyone who could write that column could run JavaScript in
an administrator's session. Content is now escaped in PHP; markup needs an explicit
opt-in and is still sanitised in the browser.
It leaked on every navigation
The map was never removed on unmount, so each Inertia page change left a live Leaflet
instance and its handlers on detached DOM. 3.0 calls map.remove() and
disconnects both observers — and a test asserts it.
Every page paid for Leaflet
2.x shipped one 74.3 kB gzipped bundle on every Nova page, map or no map. The entry bundle is now 11.2 kB; Leaflet arrives only when a map is about to become visible.
06 / When not to use this
If two number fields will do, use two number fields.
Showing a latitude and a longitude is not a reason to load a mapping library. Two
Number fields are less JavaScript, less to go wrong, and an administrator who
knows the coordinates can simply type them.
Use Nova's own fields when…
Number::make('Latitude')->step(0.000001), Number::make('Longitude')->step(0.000001),
- The coordinates are data, not a place anyone needs to see.
- Nobody is verifying the location by looking at it.
- Your index tables need a location column and nothing more.
Use FieldMap when…
FieldMap::make('Location', 'coordinates') ->editable() ->geocoder(),
- Somebody has to confirm the warehouse is on the right side of the motorway.
- Fifty branches need to be seen at once, and the gap noticed.
- The person knows where the site is, but not its coordinates.
- A delivery-zone polygon has to be checked against the estate.
Support this package
Free forever. Not free to maintain.
The work you never see is the work that keeps the map boring. Tile hosts change their URL templates. Leaflet ships a minor. Nova ships a major and the field payload shape moves under you. Somebody has to notice, upgrade, and re-run 169 tests before your admin panel renders a grey box on a Monday morning. It stays MIT either way.
Sponsoring buys that time — the compatibility passes, the issue triage, the day spent working out why one tile provider started rate-limiting. It does not buy features, because no feature is held back. It stays MIT either way.
Company tiers get your logo in the README and on this page.
Star the repo
Thirty seconds, and it is the first signal another Laravel developer looks at.
Open a good issue
A resource class, the column's contents and the browser console beat a screenshot every time.
Sponsor from $5
Recurring, cancellable, and it is what keeps this green across Nova majors.
Company tier
If your team ships this to clients, a company tier puts your logo here.
Why ask at all? This package is MIT and always will be. Nothing is paywalled, nothing phones home, and no map feature is held back for sponsors. Sponsorship buys maintenance time, not features — and if you cannot sponsor, the star and the bug report genuinely help.