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 Leaflet Geosearch Laravel Package

heloufir/filament-leaflet-geosearch

A Filament form field that combines Leaflet maps with geosearch, letting users search addresses and select coordinates visually. Easily add location picking with latitude/longitude support to your Laravel admin panels.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Confirm compatibility first. The last tagged release is from September 2022, which predates Filament v3. Check the composer.json constraints before installing; expect this to be a Filament v2-era package. On a v3/v4 project, treat installation as a spike, not a drop-in.

composer require heloufir/filament-leaflet-geosearch

2. Publish what the package ships. Most Filament field packages register a config file and/or JS/CSS assets:

php artisan vendor:publish --tag=filament-leaflet-geosearch-config
php artisan vendor:publish --tag=filament-leaflet-geosearch-assets
php artisan filament:assets

Read the published config/filament-leaflet-geosearch.php immediately — that is where the geocoding provider, API key, default map center, and zoom live. The exact key names are the first thing to verify against the README.

3. Add the field to a form. The field wraps Leaflet plus leaflet-geosearch, so it renders a map with a search box and writes the selected location back into form state.

use Heloufir\FilamentLeafletGeosearch\Fields\Geosearch;

public static function form(Form $form): Form
{
    return $form->schema([
        Geosearch::make('location'),
    ]);
}

4. Prepare storage before your first save. Decide early whether you store a formatted address, a lat,lng string, or a JSON payload — the field's state shape drives your migration and model casts.

$table->json('location')->nullable(); // or string/decimal columns
protected $casts = [
    'location' => 'array', // verify against actual field output
];

5. First use case to validate: a resource form where an admin drops a pin or searches an address, saves, reopens the record, and sees the marker restored. If hydration works, the rest of the integration is straightforward.

Implementation Patterns

Address picker with derived fields. The highest-value pattern is using the geosearch result to populate other form inputs. Make the field reactive and hook afterStateUpdated:

Geosearch::make('location')
    ->live()
    ->afterStateUpdated(function ($state, Set $set) {
        if (is_array($state)) {
            $set('latitude', $state['lat'] ?? null);
            $set('longitude', $state['lng'] ?? null);
            $set('address', $state['address'] ?? null);
        }
    }),

Confirm the actual array keys the field emits before relying on them; dump $state in a test or dd() in a local form.

Split storage for querying. If you need radius queries or map clustering later, persist latitude and longitude as separate decimal columns and keep the raw geosearch payload in a JSON column for display. Don't force everything through one opaque field value.

Per-environment provider configuration. Keep provider and key in .env and reference them from the package config. Nominatim (OSM) is fine for low-volume local/dev use; production traffic generally needs a paid provider (Google, Bing, LocationIQ) or a self-hosted geocoder. Centralize this so staging doesn't hammer the public Nominatim endpoint.

// config/filament-leaflet-geosearch.php (shape illustrative)
'provider' => env('GEOSEARCH_PROVIDER', 'osm'),
'api_key'  => env('GEOSEARCH_API_KEY'),
'center'   => [env('MAP_LAT', 31.63), env('MAP_LNG', -7.99)],
'zoom'     => 13,

Reusable field factory. If several resources need the same map behavior, wrap the field in a small static helper so center, zoom, and provider defaults stay consistent:

class MapFields
{
    public static function location(string $name = 'location'): Geosearch
    {
        return Geosearch::make($name)
            ->live()
            ->afterStateUpdated(/* shared sync logic */);
    }
}

Model accessors for clean reads. Normalize the stored payload once at the model boundary so views and API resources don't each parse it:

public function getCoordinatesAttribute(): ?array
{
    $loc = $this->location;
    return is_array($loc) && isset($loc['lat'], $loc['lng'])
        ? ['lat' => (float) $loc['lat'], 'lng' => (float) $loc['lng']]
        : null;
}

Testing. In a Livewire/Filament form test, assert the component mounts and that setting state hydrates the marker. Geocoding itself is a browser-side call, so unit tests should not depend on a live provider; test your afterStateUpdated mapping logic by setting the field state directly.

Gotchas and Tips

Version drift is the biggest risk. A 2022 release almost certainly uses the Filament v2 field API (Field, $view, asset registration via FilamentAsset). On Filament v3+ the component may not register, or may render but fail to hydrate. Pin Filament to a compatible major, or budget time to fork and port the field class and Blade view.

State shape is undocumented until you check. Whether the field stores a string, a lat,lng pair, or a JSON array determines your casts and your afterStateUpdated logic. Inspect the field's view/class (or dump state in a scratch form) before writing migrations. A wrong cast produces silent nulls on reload.

Map renders blank in tabs, modals, and collapsed sections. Leaflet needs a sized container. When the map is inside a hidden tab or a Filament modal, the map initializes at zero height. Trigger map.invalidateSize() after the tab/modal becomes visible, typically from a small Alpine hook or by listening to the relevant Filament/Livewire event.

Livewire re-renders can reset the map. Making the field live() means every keystroke or dependent update can re-render the component. If the marker jumps or the search box clears, debounce updates (.debounce(500)) and avoid unnecessary live() on unrelated sibling fields.

Nominatim will rate-limit or block you. The public OSM endpoint requires a valid identifying User-Agent/Referer and has strict usage limits; browser calls from many admin users can get you throttled. Set a proper identifier, cache results server-side if you can, or switch providers.

Client-side API keys are visible. Any provider key configured for leaflet-geosearch ships to the browser. Restrict the key by HTTP referrer/domain and never reuse a server-side secret. If that's unacceptable, proxy geocoding through your own endpoint.

Assets and CSP. If the map is unstyled or tiles don't load, check (a) php artisan filament:assets ran and the package's JS/CSS are in public/, and (b) your Content-Security-Policy allows the Leaflet CDN and tile server domains. Mixed-content errors appear when the app is HTTPS but a tile URL is HTTP.

Livewire + third-party DOM is fragile. Leaflet mutates the DOM outside Livewire's knowledge. Avoid wire:model on the map container itself; let the field own its Alpine/JS lifecycle. If you customize the Blade view, keep the Livewire state sync in the existing hooks rather than re-implementing them.

Extension points. The practical customization surfaces are: the published config (provider, key, center, zoom), subclassing the field to add fluent setters (->center(), ->zoom()), and overriding the package view via ->view('your::custom.geosearch'). Forking is reasonable for this package given its age — the field wrapper is small, and the underlying leaflet-geosearch library is the part doing real work.

Debugging checklist. Open the browser console for Leaflet/geosearch errors, watch the Network tab for the provider request and its response shape, verify the tile URLs resolve, then confirm the field's state in Livewire devtools. Most failures are one of: assets not published, provider/key misconfigured, CSP blocking, or Filament version mismatch.

Pin and isolate. Because maintenance is minimal (22 stars, single 2022 release), pin the exact version in composer.json and keep the field behind a small internal wrapper class. That way a future Filament upgrade only requires changing your wrapper, not every form.

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.
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
kda/filament-dynamic-navigation
bypersoft/signbox