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 Maps Laravel Package

larswiegers/laravel-maps

Laravel Maps lets you add Leaflet.js or Google Maps to any Laravel app with minimal setup. Supports centerpoints, markers, bounds, zoom levels, and custom tiles like OpenStreetMap and Mapbox.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and see it render in ~5 minutes.

composer require larswiegers/laravel-maps
php artisan vendor:publish --provider="Larswiegers\LaravelMaps\LaravelMapsServiceProvider"

Publishing is optional — it only exposes the package's Blade views so you can override markup, script tags, and container classes. Do it early anyway; the views are the real documentation for what attributes are supported and how the JS is wired.

Credentials go in .env, not in a config file:

MAPS_GOOGLE_MAPS_ACCESS_TOKEN=your-google-key   # enable "Maps JavaScript API" in Google Cloud + billing
MAPS_MAPBOX_ACCESS_TOKEN=your-mapbox-key        # only if you swap Leaflet's tile host to Mapbox

Leaflet + OpenStreetMap needs no key at all, which makes it the right default for local development and for the first thing you ship.

First use case — drop a single marker on a page:

<x-maps-leaflet
    :centerPoint="['lat' => 52.16, 'long' => 5]"
    :zoomLevel="13"
    :markers="[['lat' => 52.1644, 'long' => 5.9856]]"
/>

Where to look first, in order:

  1. The published view for the map type you picked (resources/views/vendor/laravel-maps/...) — every attribute the component accepts is visible there.
  2. The README table of supported features per map type. Leaflet and Google Maps are not feature-parity; check the table before promising a stakeholder a "hybrid map with fit-to-bounds."
  3. CHANGELOG.md / releases — this package has shipped breaking-ish changes (unpinned Leaflet version in v0.15, deprecated google.maps.Marker → Advanced Markers in v0.20). Skim before upgrading.

The realistic day-one use case is a store/location locator: a controller hands the view a collection of addresses, the view maps them to ['lat' => ..., 'long' => ...] arrays, one component call renders the map.

Implementation Patterns

Pass markers from the controller, keep the view dumb

// Controller
public function show(Store $store)
{
    return view('stores.show', [
        'markers' => $store->locations->map(fn ($l) => [
            'lat'   => (float) $l->latitude,
            'long'  => (float) $l->longitude,
            'title' => $l->name,
        ])->all(),
    ]);
}
<x-maps-google :markers="$markers" :fitToBounds="true" :zoomLevel="6" />

Cast to float. Decimal columns come back as strings from the DB, and you do not want to discover that in a JS console.

Choose the "framing" strategy deliberately (Google only)

  • :fitToBounds="true" — zoom/pan so all markers are visible. Right choice when the marker count is unknown and you want a guaranteed full view.
  • :centerToBoundsCenter="true" — centers on the geometric midpoint but keeps your zoomLevel. Right choice when you want a consistent zoom (e.g. always a regional overview) regardless of marker spread.
  • Neither — falls back to centerPoint. Use this for a single-location page where you know exactly where the pin should sit.

Do not stack fitToBounds and centerToBoundsCenter expecting a hybrid; pick one.

Rich markers on Google Maps

<x-maps-google :markers="[[
    'lat'   => 52.1644,
    'long'  => 5.9856,
    'title' => 'HQ',
    'icon'  => 'images/pin-hq.png',
    'label' => 'Main Store',
    'info'  => 'Open 9–5, Mon–Fri',
]]" />

You can combine icon + label + info on the same marker. info gives you a click-triggered info window; label gives you always-visible text via Advanced Markers. For Leaflet, popups are the equivalent (bound to marker instances since v0.10) — but check the published view, since the attribute names differ between the two component families.

Multiple maps on one page

Leaflet only:

<x-maps-leaflet id="map-offices" :markers="$offices" />
<x-maps-leaflet id="map-warehouse" :markers="$warehouse" />

The id is required for the second and subsequent maps. Without it, both components emit the same default container/element id and the second script instance binds to the first div. Google Maps explicitly does not support multiple maps per page — if the design needs two maps, use Leaflet for both or render one Google map and a static image for the second.

React to map interactions (Leaflet)

Since v0.21 the Leaflet instance is exposed via a custom event, so you can attach your own listeners without forking the view:

document.addEventListener('leaflet:loaded', (e) => {
    // e.detail (or your published view's payload) → map.addLayer(...) / map.on('moveend', ...)
});

Check the published view for the exact event name/payload in your installed version rather than trusting the snippet — this is the sanctioned extension point and it's the one place where reading the view beats reading the README.

Custom tile hosts / branding

<x-maps-leaflet
    :attribution="'Map data &copy; <a href=&quot;https://www.openstreetmap.org/copyright&quot;>OpenStreetMap</a> contributors, Imagery &copy; Mapbox'"
/>

With MAPS_MAPBOX_ACCESS_TOKEN present, Mapbox becomes the tile host. Mapbox's terms require visible attribution; the package provides a default, so only pass :attribution when you're overriding it — and if you override, keep both OSM and Mapbox credits.

Pin the Leaflet version in long-lived apps

<x-maps-leaflet leafletVersion="1.9.4" />

Default is "latest." The v0.15 release moved from a pinned 1.7.1 to latest and broke apps. In anything with a maintenance horizon, pin explicitly in a Blade partial/component wrapper of your own so there's one place to bump it.

Wrap it once

Create resources/views/components/app-map.blade.php that hardcodes the version pin, the default zoom, your CSS classes, and any attribution, then delegates. Every feature page then uses <x-app-map> and a Leaflet/Google swap becomes a one-file change.

Gotchas and Tips

  • long, not lng. The marker key is long. Google's own docs use lng, so copy-pasted LatLng objects from elsewhere will silently render no markers — the JS reads undefined and skips the pin.
  • Markers need an explicit container height. These are absolutely-positioned Leaflet/Google canvases; if your wrapper's height is auto, the map renders as a 0px-tall invisible div. The classic symptom is "the map works on one page but not another" — it's CSS, not the package. Set a height on the component's class/style (pass-through has been supported since v0.3).
  • Escaping quotes in attributes is manual and ugly. The README's own :attribution example is malformed HTML. Prefer single-quoted PHP strings with &quot;/&copy; entities, or build the string in a variable and pass :attribution="$attribution". Never interpolate user input into these attributes — the values land inside a <script> block, so treat every string (titles, labels, info text) as an injection vector and escape it.
  • env() dies under config:cache. If you publish the views and add your own env('MAPS_...') calls, they return null in production. Read the token via a config entry (add your own config/maps.php mapping env(...)) instead.
  • Google Maps API key constraints. Restrict the key by HTTP referrer in Google Cloud, and confirm the Maps JavaScript API specifically is enabled — a key valid for Geocoding will render a grey box with "This page didn't load Google Maps correctly." Advanced Markers additionally expect a Map ID in newer Google configurations; if labels/icons render blank on Google but fine on Leaflet, that's the first thing to check.
  • Livewire is not supported out of the box. Any re-render that swaps the DOM leaves the map container dead, because the JS only initializes once on load. The README points at issue #34 for workarounds; the pragmatic pattern is wire:ignore around the component plus a dispatch-driven re-init, or extract the map into a plain Blade partial rendered outside the Livewire tree.
  • CDN dependency. Both map types pull JS from external hosts (Leaflet from its CDN, Google from maps.googleapis.com). A strict CSP, an air-gapped deployment, or a European privacy review will block this. The mitigation is publishing the views and pointing the <script>/<link> tags at self-hosted copies — another reason to publish early even if you don't override anything yet.
  • Testing is HTML-string assertions only. The project's own suite renders components and asserts on output; there are no browser tests, so a broken tile URL or a mis-sized container will pass CI and fail in the browser. Add one Pest browser/Dusk test that loads a page with a map and asserts the container reaches a non-zero size and the marker count in the DOM.
  • Publishing views is the intended fork point. For anything beyond attribute tweaks — custom clustering, custom controls, a dark-mode tile layer — publish and edit the view rather than reaching into vendor/. Vendor edits vanish on composer update and are invisible in code review.
  • Version drift is the real long-term risk. Laravel 10/11/12/13 support landed via separate minor releases, and the Google marker implementation changed underneath a stable-looking API (v0.20). Pin the package version in composer.json, read the release notes before bumping, and keep your <x-app-map> wrapper as the single seam that absorbs a Leaflet↔Google swap or a major-version jump.
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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console