fahiem/filament-pinpoint
A Filament 4 & 5 location picker field with Google Maps or Leaflet.js (OpenStreetMap) support, offering search, click-to-set and draggable markers, radius display, reverse geocoding, dark mode, and translations.
Architecture fit
fahiem/filament-pinpoint is a UI-layer package, not a data-layer one. It ships two Filament components — Pinpoint (form field) and PinpointEntry (infolist entry) — that extend Filament's existing component contracts and write coordinates into your columns via latField() / lngField(). There are no migrations, models, tables, or background jobs to own. That is the ideal shape for a third-party dependency: it fails, you delete the composer line, and your schema is untouched.
Fit is strong only if the host application is already on Filament 4/5 with Livewire-driven resource forms. In that context it removes a genuinely non-trivial build (map rendering, marker drag state, Places/Nominatim search, reverse geocode → field fan-out, dark mode, Repeater-scoped field paths). Outside Filament — Inertia/Vue, Livewire-only, or a plain Blade admin — it is unusable and you would be re-implementing the same component from scratch.
Worth noting: the reverse-geocode field mapping (provinceField, cityField, districtField, villageField, postalCodeField) is an Indonesian administrative hierarchy. If your address model is Western (state/postcode/line1-line2) or a generic json blob, expect mapping friction — the package's opinion about address structure is part of the product, not just a config.
Integration feasibility Low-effort, reversible:
composer require fahiem/filament-pinpointPINPOINT_PROVIDER, plus GOOGLE_MAPS_API_KEY only for Google)vendor:publish for defaultslat/lng (decimal 10,7 / 10,7 or DECIMAL(10,8)/DECIMAL(11,8)) plus any auto-filled address columnsPinpoint::make('location')->latField('lat')->lngField('lng') into the resource formA single Laravel developer can have a working field in an afternoon and a production-ready one in two to three days including QA. The Leaflet path requires no account, no key, and no procurement — which is unusual and materially reduces time-to-first-demo.
Technical risk
| Risk | Severity | Notes |
|---|---|---|
| Bus factor of one | High | 19 stars, 0 dependents, single maintainer. No ecosystem evidence of production use at scale. |
| Filament major churn | High | Filament ships breaking majors fast; the package claims 4 & 5 support. A Filament 6 upgrade could strand you. |
| Client-side coordinate trust | Medium–High | Marker position originates in the browser. Nothing in the package validates lat/lng server-side or enforces a bounding region. |
| Nominatim usage policy | Medium | Public Nominatim is rate-limited (~1 req/s), requires an identifying User-Agent, and prohibits heavy/commercial use. Default config points at the public instance. |
| Google API key exposure | Medium | Maps JS keys are visible in the page; requires HTTP-referrer + API restrictions and billing alerts, or a proxy. |
| No spatial query layer | Medium | Package gives you a point and a radius value; it does not give you PostGIS, spatial indexes, or "nearby" queries. You own that. |
| Third-party JS runtime | Medium | Map depends on runtime loading of Google Maps JS or OSM tiles. CSP, ad-blockers, and network egress policy can break it invisibly. |
| Release recency | Low | Active release cadence and changelog present — a positive signal. |
Key questions
province/city/district/village fan-out, or do we map only address + postalCode?Stack fit
Resource::form() and Resource::infolist() schemas with no panel-level changes.data.branches.0.latitude) mean multi-location entities (branches, warehouses, delivery zones) don't require a custom relation manager.PinpointEntry with pins([...]), per-pin color/icon/info HTML, and fitBounds() covers read-only dashboards — a second deliverable for free.Migration path Additive only; no destructive step.
lat/lng (DECIMAL(10,7) is sufficient for ~1cm precision) and the subset of address columns you actually intend to auto-fill.Compatibility
^1.1) and add a CI job that upgrades Filament minors against a smoke test rendering the field.->provider('google')) only for the screens where Places Autocomplete demonstrably improves address capture — the provider is settable at .env and per instance, so you can be selective.connect-src / img-src / script-src for maps.googleapis.com, *.tile.openstreetmap.org, and nominatim.openstreetmap.org in the same PR — silent CSP blocks are the most common "it works locally" failure.->readOnlyOn('view') on view pages to prevent accidental edits from read-only screens.Sequencing
*Field() hooks you activate; document that reverse geocoding is best-effort.Server-side hardening (do not skip)
The package is a capture widget. Add your own FormRequest validation: lat between -90/90, lng between -180/180, and — if your business is region-bound — a bounding-box check that rejects points outside the serviceable area. Without this, a crafted Livewire payload can persist arbitrary coordinates.
Maintenance
Pinpoint::make() in a thin internal factory (App\Filament\Fields\LocationPicker) so a swap or a fork touches one file. Cheap insurance against bus-factor-one.Support
Scaling
NOMINATIM_URL makes the self-hosted swap a one-line change — verify the instance is set up and monitored before you need it.Failure modes
| Failure | User-visible effect | Detection | Mitigation |
|---|---|---|---|
| Google key invalid / unreferrer'd | Blank map; form still submits | Client-side only — no server error | Synthetic browser check in CI/staging; alert on Maps API error-rate |
| Google quota exhausted / billing lapse | Blank map, no search | Billing alerts | Budget alerts at 50/80/100%, quota caps |
| Nominatim 403 / rate-limited | Search silently returns nothing | No error surfaced | Self-host or fall back to Google; log client-side geocode failures |
| CSP blocks tiles/scripts | Grey map, no pins | Manual QA only | CSP set in same PR; e2e smoke test |
| Ad-blocker / corp firewall | Same as CSP | Support tickets | Documented as known limitation |
| Malformed coordinates persisted | Bad data, silent | Server validation | Range + bounding-box rules |
| Package abandoned / broken on Filament 6 | Field fails to render on upgrade | CI smoke test | Internal wrapper class; fork plan |
| Repeater path drift | Coordinates written to wrong column | Integration test | Assert DB values in a Repeater test, not just render |
Ramp-up
How can I help you explore Laravel packages today?