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

Laravel Map Laravel Package

bagusindrayana/laravel-map

Laravel Map is a lightweight package for adding interactive maps to Laravel applications, with Blade components, configurable markers and popups, and a simple API for rendering maps from coordinates or addresses with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish everything the package ships (config, views, and the Leaflet/JS assets), because nothing works until the frontend assets exist under public/vendor/:

composer require bagusindrayana/laravel-map
php artisan vendor:publish --provider="Bagusindrayana\LaravelMap\LaravelMapServiceProvider"
composer dump-autoload -o
php artisan optimize:clear

The provider/namespace string is the first thing to confirm — v1.0.6 changed the PSR-4 name, so check the package's composer.json autoload.psr-4 block and match the provider class exactly. A wrong provider string means vendor:publish silently publishes nothing.

Where to look first, in order:

  1. config/laravel-map.php (or whatever the published config is named) — center lat/lng, zoom, tile provider URL and attribution, asset paths. This is what you will change most often.
  2. resources/views/vendor/laravel-map/ — the published Blade view/component that emits the map container and the init script. Read it before you try to style or wrap it.
  3. public/vendor/laravel-map/ — the bundled Leaflet and package JS. If these are missing, everything else is moot.
  4. The package source for the v1.0.6 fetch method — read it to learn whether it lives server-side (a facade/service returning JSON config) or client-side (a JS helper). Its contract determines your dynamic-map design.

First use case — the one you should get working before anything else: render one marker from an Eloquent model.

// Controller
public function show(Store $store)
{
    return view('stores.show', [
        'store' => $store,
        'map' => $store->only(['latitude', 'longitude', 'name']),
    ]);
}
{{-- resources/views/stores/show.blade.php --}}
@push('styles')
    <link rel="stylesheet" href="{{ asset('vendor/laravel-map/leaflet.css') }}">
@endpush

<x-laravel-map :center="[$map['latitude'], $map['longitude']]" :zoom="14" />

@push('scripts')
    <script src="{{ asset('vendor/laravel-map/leaflet.js') }}"></script>
@endpush

Exact component/directive names and asset filenames vary — copy them from the published view rather than guessing. Confirm one marker renders on a real page before wiring a list of models. If that single marker works, the tiles, asset paths, and config are all correct, and every later problem is data-shape or JS, not installation.

Because the package is archived (last release 2021-12-28, 4 stars), treat installation as "vendor it deliberately": pin the exact version in composer.json, commit the published assets, and note in your README that the package is unmaintained. Day-to-day leverage then comes from the small stable surface (config + Blade + a bit of JS), not from upstream fixes.

Implementation Patterns

Controller shapes the data; Blade only renders. Keep the map dumb. Build a plain array or JsonResource in the controller and hand it to the component. Do not call Model::all() from a Blade template or loop over Eloquent models inside the view — you lose N+1 visibility and you cannot test the shape.

// A stable marker shape you can reuse for both Blade and JSON
private function markers(Collection $stores): array
{
    return $stores->map(fn ($s) => [
        'lat'   => (float) $s->latitude,
        'lng'   => (float) $s->longitude,
        'label' => $s->name,
        'url'   => route('stores.show', $s),
    ])->all();
}

Use this same mapper for the server-rendered component and for the fetch endpoint so markers look identical however they arrive.

Model plotting via latitude/longitude. The package accepts Eloquent models and reads latitude/longitude attributes. If your columns are named differently (lat/lng, geo_lat, casts to Point), do not rename your schema to suit the package — map to the expected keys in the controller as above. Keep the model's raw attributes out of the view.

Static map (server-rendered, recommended default). For dashboards, detail pages, and admin views with a bounded marker set, render everything in the initial page load. Fewer moving parts, no endpoint to secure, no empty-state flicker. Cache the page fragment if the query is expensive.

Dynamic map via fetch. Use v1.0.6's fetch path when markers depend on user interaction (filters, search, bounding-box pan, pagination) or when the marker set is large. Give it a dedicated, versioned JSON route:

Route::get('/maps/{map}/markers', [MapMarkerController::class, 'index'])
    ->name('maps.markers');

// controller
public function index(Request $request, Map $map)
{
    $this->authorize('view', $map);

    return response()->json([
        'data' => $this->markers(
            $map->stores()
                ->when($request->query('q'), fn ($q, $term) => $q->where('name', 'like', "%{$term}%"))
                ->limit(500)
                ->get()
        ),
    ])->header('Cache-Control', 'private, max-age=30');
}

Freeze the response contract (data[].lat, .lng, .label, .url) and document it next to the endpoint — the package JS is coupled to those keys, and a rename breaks rendering without a server error.

Guard against double-initialization. In Livewire, Turbo, or any partial-reload setup, wrap asset pushes and map init in @once and initialize in an updated/mounted hook, not on every render. Leaflet initialized twice on the same container produces a broken, grey, or doubled map rather than a clear error.

@once
    @push('scripts')
        <script src="{{ asset('vendor/laravel-map/leaflet.js') }}"></script>
    @endpush
@endonce

Vite vs. published assets — pick one. If your app already bundles Leaflet through Vite, do not also load the package's copy. Either import Leaflet in app.js and let the package only supply config/markup, or use the published vendor files and keep Leaflet out of your bundle. Two Leaflet instances on one page is the single most common cause of a "map renders but markers/controls are dead" bug.

Livewire / Inertia.

  • Livewire: render the container in Blade; init on mounted, and on filter changes dispatch a browser event ($this->dispatch('markers-updated', markers: $markers)) that your JS listens for — then call the map's marker layer update, don't re-init.
  • Inertia: keep the map in a persistent component; use the fetch pattern on prop changes instead of a full page visit, and clean up the Leaflet instance in onUnmounted (or before re-init) to avoid leaked handlers.

Testing. Feature-test the JSON endpoint — status, auth scoping, payload shape, and that a forbidden model returns 403. You cannot meaningfully test Leaflet rendering in PHPUnit; keep a documented manual smoke checklist (map renders, tiles load, markers clickable, fetch returns after a filter change) and run it on every dependency or asset change.

Gotchas and Tips

  • The archived status is the real gotcha. No upstream fixes, no security patches for the bundled Leaflet, single-maintainer history. Pin the exact tag, commit published assets into your repo, and assign an owner. If you need a fix, plan to fork or patch in place — budget for that rather than filing an issue.

  • PSR-4 rename broke more than the provider string. Any reference to the old namespace — a facade alias in config/app.php, use statements in your code, fully-qualified class names inside published views, cached config — fails with a class-not-found or, worse, renders an empty map. Before upgrading, grep: grep -rn "Old\\\\Namespace" app/ config/ resources/views/.

  • Published assets never update themselves. The v1.0.6 Leaflet and JS bumps only reach your app if you re-publish with --force:

    php artisan vendor:publish --tag=laravel-map-assets --force
    

    Then cache-bust (asset() with a version query, or Vite hashing) so browsers don't serve the old JS against new markup.

  • Leaflet version jump can break add-ons. If you added marker clustering, draw tools, or heatmap plugins on top of the bundled Leaflet, retest them. Plugins are the usual casualty of a Leaflet bump, and you will not get an upstream compatibility note.

  • fetch failures are silent by default. Handle non-200s, malformed JSON, and empty arrays explicitly and show an error/empty state — otherwise the user sees a blank map with no clue why. Include the CSRF token header for any POST-based fetch, set credentials/X-Requested-With as the endpoint expects, and set cache headers for slow-moving data.

  • CSRF 419s on dynamic maps. A fetch hitting a web-middleware route without the token will 419 and — if errors are swallowed — look like "no markers." Either use a GET endpoint (preferred for map data) or read the token from <meta name="csrf-token">.

  • Tile provider is a hidden dependency. Gray tiles usually mean a missing/invalid API key or an exhausted quota, not a bug in the package. Put the tile URL and key in config/env, and log or surface a visible message when tiles fail — this presents as a plain grey background far more often than as an error.

  • Clear caches after any namespace or config change. php artisan config:clear view:clear route:clear — stale cached config pointing at an old namespace is the classic "works locally, dead in staging" failure.

  • Debugging order. Browser console (JS/Leaflet errors, double-load warnings) → Network tab (the fetch request, status, payload shape) → storage/logs/laravel.log (server-side) → php artisan about to confirm the installed version matches what you think. Do not start in the package source; it is short and rarely the culprit.

  • Performance ceiling. Leaflet is client-side, so server scaling is not the concern. Thousands of markers need clustering (add a Leaflet plugin, which reintroduces the version-compatibility risk above), and large GeoJSON payloads need server-side pagination or bbox filtering in the fetch endpoint. Cache slow queries.

  • Extension points — prefer the safe ones. Change behavior through the published config and published Blade views, not by editing public/vendor/... or the package's JS bundle. For custom marker rendering (icons, popups with HTML, badges), write your own wrapper Blade partial or a thin component that composes the package's component, and pass a pre-built marker array. That keeps your code upgradeable and makes a future fork straightforward.

  • Separate your risks. If you ever touch both the namespace/asset upgrade and the fetch integration, do it in two commits/releases. A blank map after a combined change is much harder to attribute.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource