filament-map-picker provides Filament 5 form, infolist, and widget components powered by Leaflet.js. Use it to select a point location in forms, draw one or more map areas as GeoJSON, and display saved locations in read-only infolists or dashboard widgets.
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().
->tiles([...])drag mode and click mode for the point markerInstall the package with Composer:
composer require darkclow4/filament-map-picker
Then publish Filament assets:
php artisan filament:assets
The package supports Laravel auto-discovery through:
Darkclow4\FilamentMapPicker\FilamentMapPickerServiceProvider::class
In most applications, no manual provider registration is needed.
By default, the field stores point coordinates as:
[
'lat' => -6.2,
'lng' => 106.816666,
]
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.
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);
});
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');
Use MapEntry when you want to display saved coordinates on view/detail pages.
Add ->interactive() if you want the user to pan and zoom around the point without changing the saved state.
use Darkclow4\FilamentMapPicker\Infolists\Components\MapEntry;
MapEntry::make('location')
->state(fn ($record): array => [
'lat' => (float) $record->latitude,
'lng' => (float) $record->longitude,
])
->interactive()
->height(360)
->zoom(15);
For saved GeoJSON areas, enable ->drawable() and include geojson in the state:
use Darkclow4\FilamentMapPicker\Infolists\Components\MapEntry;
MapEntry::make('service_area')
->label('Service Area')
->drawable()
->interactive()
->showAreaMeasurement()
->areaMeasurementUnit('ha')
->state(fn ($record): array => [
'lat' => (float) $record->latitude,
'lng' => (float) $record->longitude,
'geojson' => $record->service_area_geojson ?? [
'type' => 'FeatureCollection',
'features' => [],
],
])
->height(420);
Use MapWidget directly when the state is passed from a page or dashboard:
use Darkclow4\FilamentMapPicker\Widgets\MapWidget;
protected function getHeaderWidgets(): array
{
return [
MapWidget::make([
'heading' => 'Main Store',
'description' => 'Latest saved branch location',
'state' => [
'lat' => -6.2,
'lng' => 106.816666,
],
]),
];
}
For dynamic configuration, create your own widget class and extend the package widget:
namespace App\Filament\Widgets;
use Darkclow4\FilamentMapPicker\Widgets\MapWidget;
class BranchMapWidget extends MapWidget
{
public ?string $heading = 'Branch Coverage';
public ?string $description = 'Primary operating area';
public function mount(): void
{
$this
->drawable()
->showAreaMeasurement()
->areaMeasurementUnit('ha')
->height(460)
->zoom(12);
$this->state = [
'lat' => -6.2,
'lng' => 106.816666,
'geojson' => [
'type' => 'FeatureCollection',
'features' => [],
],
];
}
}
MapPicker::make('location')->mode('drag')
MapPicker::make('location')->mode('click')
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',
],
],
]
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:
->tiles() is not called, only osm is available->tile() is not called, osm is selected by defaultosmosm tile is always included->showDefaultTile(false) if you want to hide the built-in osm tile and only expose your custom tilesEnable geocoding search when you want users to jump quickly to a location:
MapPicker::make('location')
->searchable()
->searchPlaceholder('Search for a place')
->searchResultLimit(8)
Behavior:
By default, search uses:
https://nominatim.openstreetmap.org/search
You can override it:
->searchProviderUrl('https://nominatim.openstreetmap.org/search')
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:
['lat', 'lng', 'geojson']geojson->multipleShapes() if you want multiple polygons / rectangles / circles in the same FeatureCollectionafterStateUpdated() / afterStateHydrated() to map latitude, longitude, and geojson into separate database columnsUnless noted otherwise, these configuration methods are available on:
Darkclow4\FilamentMapPicker\Forms\Components\MapPickerDarkclow4\FilamentMapPicker\Infolists\Components\MapEntryDarkclow4\FilamentMapPicker\Widgets\MapWidget->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)
->interactive(bool $condition = true)Allows a read-only map to remain draggable and zoomable without syncing state back to Livewire.
->interactive()
->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. This is only available on the form field.
->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')
latitude, longitude, or area GeoJSONThis package is open-sourced software licensed under the MIT license.
How can I help you explore Laravel packages today?