Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Geometry Laravel Package

swisnl/filament-geometry

A Filament form field for drawing and editing map geometries. Let users place points, draw lines, and outline polygons on an interactive map, then save the result as GeoJSON in your database. Adds spatial input to your admin panel with simple, familiar field configuration and no custom JavaScript re...

View on GitHub
Deep Wiki
Context7

Getting Started

swisnl/filament-geometry adds a map-backed geometry field to Filament forms: the user draws a point, line, or polygon on a Leaflet map, and the field hands back GeoJSON you can persist in a JSON column. Version 0.8.1 is the first release with Filament 5.x compatibility, so it is the minimum you should pin on a Filament 5 app.

composer require swisnl/filament-geometry:^0.8.1

If the map renders blank after install, it is almost always assets rather than code: run php artisan filament:assets, then php artisan view:clear, and hard-refresh the browser. If you are on Filament 4.x, 0.8.0 is functionally equivalent — upgrade anyway so you stay on one constraint when you move to 5.

Where to look first:

  1. The package README for the field's fluent API (it is short and the field is the whole package).
  2. vendor/swisnl/filament-geometry/src — the field class and its Blade view; this is where the map options, Leaflet init, and state hydration live.
  3. Your migration + model cast, because the package is only half the integration: the other half is how you store and read GeoJSON.

First use case — a "draw the area" field on a resource:

use SwisNl\FilamentGeometry\Forms\Components\Geometry;

Geometry::make('area')
    ->label('Service area')
    ->polygon()
    ->required(),
// migration
$table->json('area')->nullable();

// model
protected $casts = ['area' => 'array'];

Render it, save it, and confirm the value round-trips through the edit form before you build anything else on top.

Implementation Patterns

Restrict the geometry type per field. Most real applications want exactly one shape kind per field — a delivery zone is a polygon, a pickup point is a point. Use the field's type helpers rather than accepting everything and validating afterwards; you get a better editing UX and simpler stored data.

Normalise the stored shape. Decide early whether you persist a bare geometry ({"type":"Polygon","coordinates":[...]}) or a full Feature. A Feature with a properties bag is far more flexible (name, colour, metadata) and costs nothing extra. Wrap the field in a small accessor or custom cast so every consumer of the model sees the same structure.

Map defaults live in code, not in the DB. Centre, zoom, and tile layer are field configuration. Put your default centre/zoom in a config file or a small helper so every geometry field in the app is consistent, and override per-field only where the geography genuinely differs (e.g. a regional office map vs. a nationwide one).

Table/infolist display. Geometry is hard to read as raw JSON in a table. Either render a compact map in an infolist entry, or show a human summary (point count, area, centroid) computed from the GeoJSON and keep the map for the view page. Do not dump the raw array into a text column — it will destroy table layout.

Spatial queries need a second column. GeoJSON in a JSON column is a blob: ST_Contains, ST_Intersects, and bounding-box indexes will not help you. If you need "which zone contains this lat/lng?", maintain a parallel native geometry/geography column (MySQL 8 or Postgres + PostGIS) written from the same form data — via a model observer or a saving hook — and query that. Keep the JSON as the source of truth for the editor and the spatial column as the index.

Testing. In a Livewire/Filament form test, set the field state to a fixture GeoJSON array and assert persistence and validation. Keep a handful of fixtures in tests/Fixtures (a point, a simple polygon, a self-intersecting polygon) — they double as regression data if the field's state format ever changes between 0.x releases.

Seeding and imports. When you seed demo data or import shapes from another system, write through the same normalisation path the field produces. Imported geometries that skip that step are the number one source of "the map shows nothing but the data looks fine" bugs.

Gotchas and Tips

  • Filament 5 requires >= 0.8.1. This release exists solely to add that compatibility. If you are on Filament 5 and pinned 0.8.0, composer may resolve but the field will break at render time. Bump the constraint, then run php artisan filament:assets — the compiled map assets from the older build stay in public/ and can mask or cause the upgrade.
  • This is 0.x. There is no semver BC promise. Pin with ~0.8.1 and read every changelog; the Filament major-version jump in a patch release is the pattern to expect.
  • Coordinate order. GeoJSON is [longitude, latitude]; Leaflet is [latitude, longitude]. Every hand-written fixture, every import mapping, and every "my marker is in the ocean off Africa" report comes from this. Convert in exactly one place.
  • Tiles in production. The default OpenStreetMap tiles are not intended for production traffic and will be rate-limited or blocked. Point the field at your own provider (MapTiler, Mapbox, self-hosted) and set the attribution string — both legal and practical.
  • Maps inside modals, tabs, and repeaters. Leaflet needs a non-zero container size at init. In a modal or a collapsed tab the map can mount at 0x0 and render grey. If you hit it, the usual fix is triggering invalidateSize() on the map after the container becomes visible — check whether the field's view already does this before patching it, and if not, this is a legitimate extension point (publish the view and add it).
  • CSP and dark mode. A strict Content-Security-Policy needs img-src for your tile host and may need style-src 'unsafe-inline' for Leaflet's positioning. In dark-mode admin panels the map will be a bright rectangle — a CSS filter on the tile layer is the cheap fix.
  • Empty vs. null. Decide whether "nothing drawn" saves as null or {}/[] and enforce it, otherwise your spatial sync and your validation rules will disagree about whether the record has geometry.
  • Vertex count. Freehand drawing can produce thousands of points. Simplify (Douglas–Peucker) on save if you store into a spatial column or transmit it to clients; a multi-megabyte polygon column is a slow query waiting to happen.
  • Debugging tip. When the map misbehaves, inspect the Livewire component state directly ($wire.get('data.area')) rather than the rendered DOM — it separates "the field didn't capture the drawing" from "the map didn't render", which are entirely different problems in this package.
  • Extension points. The Blade view is publishable, which is the intended route for swapping tile providers' UI, adding a "clear" button, or injecting invalidateSize(). Prefer that over subclassing the field, so package upgrades stay reviewable.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package