darkclow4 / filament-map-picker
Leaflet map picker and GeoJSON draw field for Filament 5.
Package info
github.com/darkclow4/filament-map-picker
Language:JavaScript
pkg:composer/darkclow4/filament-map-picker
Requires
- php: ^8.3
- filament/filament: ^5.0
- illuminate/contracts: ^13.0
- spatie/laravel-package-tools: ^1.92
Suggests
- ext-json: Required for working with JSON state and custom tile configuration.
This package is not auto-updated.
Last update: 2026-05-08 02:33:05 UTC
README
filament-map-picker is a custom field for Filament 5 that provides an interactive Leaflet.js map for selecting a point location and, optionally, drawing one or more map areas as GeoJSON.
The package is designed to stay flexible. It does not force how you persist latitude, longitude, or area data. Instead, it keeps a predictable field state and lets you map the values however you want through afterStateUpdated() and afterStateHydrated().
Features
- Built for Filament 5
- Powered by locally bundled Leaflet.js and Leaflet Draw
- Default OpenStreetMap tile layer
- Custom tile support through
->tiles([...]) dragmode andclickmode for the point marker- Optional geocoding search UI
- Optional polygon, rectangle, and circle drawing tools
- Optional multi-shape GeoJSON collections
- Combined point + area state in a single field
- Reactive Livewire-friendly state updates
- Alpine.js powered interaction
- Leaflet layers control for switching tile layers
Requirements
- PHP 8.3+
- Laravel 13+
- Filament 5
Installation
Install the package with Composer:
composer require darkclow4/filament-map-picker
Then publish Filament assets:
php artisan filament:assets
Package Registration
The package supports Laravel auto-discovery through:
Darkclow4\FilamentMapPicker\FilamentMapPickerServiceProvider::class
In most applications, no manual provider registration is needed.
State Format
Point-only mode
By default, the field stores point coordinates as:
[
'lat' => -6.2,
'lng' => 106.816666,
]
Point + draw mode
When draw mode is enabled, the field stores a combined state:
[
'lat' => -6.2,
'lng' => 106.816666,
'geojson' => [
'type' => 'FeatureCollection',
'features' => [
// polygon / rectangle / circle features
],
],
]
This makes it easy to keep a marker location and one or more service-area shapes in the same field while still saving them into separate database columns.
Basic Usage
Point-only field
use Darkclow4\FilamentMapPicker\Forms\Components\MapPicker; use Filament\Schemas\Components\Utilities\Set; MapPicker::make('location') ->mode('click') ->defaultLocation(-6.2, 106.816666) ->zoom(13) ->height(420) ->live() ->afterStateUpdated(function (?array $state, Set $set): void { $set('latitude', $state['lat'] ?? null); $set('longitude', $state['lng'] ?? null); });
Point + area in a single field
use Darkclow4\FilamentMapPicker\Forms\Components\MapPicker; use Filament\Forms\Components\Hidden; use Filament\Schemas\Components\Utilities\Get; use Filament\Schemas\Components\Utilities\Set; MapPicker::make('location') ->mode('drag') ->defaultLocation(-6.2, 106.816666) ->drawable() ->drawTools(['polygon', 'rectangle', 'circle']) ->multipleShapes() ->fitDrawBounds(false) ->height(480) ->live() ->afterStateHydrated(function (MapPicker $component, Get $get): void { $component->state([ 'lat' => is_numeric($get('latitude')) ? (float) $get('latitude') : -6.2, 'lng' => is_numeric($get('longitude')) ? (float) $get('longitude') : 106.816666, 'geojson' => is_array($get('area')) ? $get('area') : [ 'type' => 'FeatureCollection', 'features' => [], ], ]); }) ->afterStateUpdated(function (?array $state, Set $set): void { $set('latitude', $state['lat'] ?? null); $set('longitude', $state['lng'] ?? null); $set('area', $state['geojson'] ?? [ 'type' => 'FeatureCollection', 'features' => [], ]); }); Hidden::make('area');
Selection Modes
Drag mode
MapPicker::make('location')->mode('drag')
- the user drags the map
- the marker stays centered
- coordinates are taken from the map center
Click mode
MapPicker::make('location')->mode('click')
- the user clicks on the map
- the marker moves to the clicked point
- coordinates are taken from that point
Default Tile Layer
If you do not provide custom tiles, the field uses OpenStreetMap automatically.
MapPicker::make('location') ->tile('osm')
Built-in default tile definition:
[
'osm' => [
'label' => 'OpenStreetMap',
'url' => 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
'options' => [
'maxZoom' => 19,
'attribution' => '© OpenStreetMap contributors',
],
],
]
Custom Tile Layers
You can provide your own tile layers with ->tiles([...]). Those definitions are serialized to JSON and passed directly to Leaflet.
MapPicker::make('location') ->tiles([ 'opentopo' => [ 'label' => 'Open Topo Map', 'url' => 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', 'options' => [ 'maxZoom' => 19, 'attribution' => 'Map data: © OpenStreetMap contributors | Map style: © OpenTopoMap', ], ], ]) ->tile('opentopo');
Tile behavior:
- if
->tiles()is not called, onlyosmis available - if
->tile()is not called,osmis selected by default - if the selected tile key is invalid, the field falls back to
osm - if more than one base layer exists, Leaflet's built-in layers control is shown on the map
- by default the built-in
osmtile is always included - use
->showDefaultTile(false)if you want to hide the built-inosmtile and only expose your custom tiles
Searchable Mode
Enable geocoding search when you want users to jump quickly to a location:
MapPicker::make('location') ->searchable() ->searchPlaceholder('Search for a place') ->searchResultLimit(8)
Behavior:
- users can search for a place or address
- matching results appear in a dropdown list
- selecting a result recenters the map and updates the field state
- selected results are highlighted when reopened
By default, search uses:
https://nominatim.openstreetmap.org/search
You can override it:
->searchProviderUrl('https://nominatim.openstreetmap.org/search')
Drawing Shapes as GeoJSON
Enable draw mode when you want the user to define areas while keeping a marker location in the same field state.
MapPicker::make('location') ->drawable() ->drawTools(['polygon', 'rectangle', 'circle']) ->multipleShapes() ->fitDrawBounds(false)
Notes:
- draw mode stores the field state as
['lat', 'lng', 'geojson'] - the marker location still works as normal
- the drawn area is stored inside
geojson - by default only one drawn shape is kept at a time
- use
->multipleShapes()if you want multiple polygons / rectangles / circles in the sameFeatureCollection - editing or deleting shapes updates the field state automatically
- use
afterStateUpdated()/afterStateHydrated()to maplatitude,longitude, andgeojsoninto separate database columns
API Reference
->tile(string $tile)
Sets the active tile key.
->tile('osm')
->tiles(array $tiles)
Registers custom tile layers.
Expected tile format:
[
'custom-key' => [
'label' => 'Tile Name',
'url' => 'https://example.com/{z}/{x}/{y}.png',
'options' => [
'maxZoom' => 20,
'attribution' => '...'
],
],
]
->showDefaultTile(bool $condition = true)
Controls whether the built-in osm tile should be included in the available tile list.
->showDefaultTile(false)
->mode('drag|click')
Defines the point marker interaction mode.
->mode('drag') ->mode('click')
->defaultLocation(float $lat, float $lng)
Sets the initial map coordinates.
->defaultLocation(-6.2, 106.816666)
->zoom(int $level)
Sets the initial map zoom level.
->zoom(13)
->height(int|string $height)
Sets the map container height.
->height(420) ->height('32rem') ->height('60vh')
->markerColor(string $color)
Sets the marker color used in both drag and click modes.
->markerColor('#0f766e') ->markerColor('rgb(37, 99, 235)')
->searchable(bool $condition = true)
Enables place search.
->searchable()
->searchPlaceholder(string $placeholder)
Overrides the search input placeholder.
->searchPlaceholder('Search for a city or address')
->searchProviderUrl(string $url)
Overrides the geocoding endpoint used by the search box.
->searchProviderUrl('https://nominatim.openstreetmap.org/search')
->searchResultLimit(int $limit)
Controls how many search results are shown in the dropdown.
->searchResultLimit(8)
->drawable(bool $condition = true)
Enables shape drawing tools and switches the field state into combined point + GeoJSON mode.
->drawable()
->drawTools(array $tools)
Controls which draw tools are available. Supported values are polygon, rectangle, and circle.
->drawTools(['polygon', 'rectangle'])
->fitDrawBounds(bool $condition = true)
Controls whether the map should automatically fit to the drawn shapes after create or edit.
->fitDrawBounds(false)
->multipleShapes(bool $condition = true)
Allows the field to keep multiple drawn shapes inside the GeoJSON FeatureCollection.
->multipleShapes()
->showAreaMeasurement(bool $condition = true)
Shows area labels directly inside the map for each drawn shape when draw mode is enabled.
->showAreaMeasurement()
->areaMeasurementUnit('m2'|'ha')
Controls the unit used for the displayed area measurement.
->areaMeasurementUnit('m2') ->areaMeasurementUnit('ha')
Notes
- The package does not force any persistence strategy for
latitude,longitude, or area GeoJSON - You are free to map the field state into any schema structure you prefer
- Custom tiles are the developer's responsibility, including availability and usage terms
- Search providers are the developer's responsibility, including availability and usage terms
License
This package is open-sourced software licensed under the MIT license.