kwasii/livewire-mapcn
A Livewire component for embedding interactive MapCN maps in Laravel apps. Drop a map into any Blade view, bind center coordinates, zoom, and markers to Livewire properties, and let the map update reactively as state changes—no manual JavaScript wiring required.
Architecture fit. The package is a Blade component layer over MapLibre GL JS that speaks Livewire's reactivity model. That maps cleanly onto a Laravel monolith where views are server-rendered and state lives in PHP — markers, routes, arcs, GeoJSON, clusters and viewport state are declared in Blade from Eloquent-backed properties, and the package handles the hard part: reconciling MapLibre layers in place on Livewire re-render, preserving the camera (wire:ignore.self), forwarding MapLibre events back into PHP listeners, and tearing down WebGL contexts on wire:navigate. That reconciliation layer is the actual purchase; the renderer itself is free and permissively licensed. It is a poor fit for anything requiring a JS island, drawing/editing tools, offline tiles, sub-100ms high-frequency interaction, or custom vector styling — those want raw MapLibre or a dedicated JS layer.
Integration feasibility. High. composer require, two Blade directives (@livewireMapStyles, @livewireMapScripts), and markup — no Node pipeline, no npm lockfile, and Tailwind is no longer required. Config and assets are optionally published but served by default. A working map is an afternoon; a feature-complete surface with clustering, routes, and popups is days. There is a bundled skill/development guide with a full component hierarchy and configuration reference, which materially lowers the ramp cost.
Technical risk. The dominant risk is vendor, not technical. The package is young (25 stars, 0 dependents, single maintainer) with no public downstream proof of production use. v2 is a breaking release with a real bug-fix list, including two security- and reliability-relevant items: cluster popups previously failed to escape feature properties (potential injection vector on any map rendering user-supplied strings) and every map event reached Livewire listeners twice, doubling server requests. That is a positive maturity signal — documented upgrade guide, honest changelog — but it also confirms the code is still settling. Secondary risks: MapLibre v6 is ESM-only, so the CDN/asset-loading path (cdn_url, cdn_css_url, published config) is a concrete failure mode for anyone who published config on v1; and the defaults lean on a public OSRM demo router (session-cached, 10s timeout, no SLA) and CARTO basemaps whose config is flagged non-commercial, which is a licensing review item before any public launch. Finally, the coordinate contract is [lng, lat] — inverted from the intuitive order — which produces silently wrong pins rather than errors, a class of defect that survives QA and reaches users.
Key questions.
composer.json before committing a sprint.osrm_url at our own router before launch?Stack fit. Laravel 11–13 + Livewire 3/4 on PHP 8.3+, Alpine (ships with Livewire), no Node. If the app is Livewire, this is close to a drop-in capability; if the frontend is React/Vue/Inertia, the entire value proposition evaporates and this should not be shortlisted. The component surface — <x-map>, <x-map-marker>, <x-map-popup>, <x-map-cluster-layer>, <x-map-route>, <x-map-route-marker>, <x-map-arc>, <x-map-geojson>, <x-map-controls>/<x-map-control> — covers read-mostly, state-driven map UI with no custom JavaScript.
Migration path (v1 → v2, if applicable). Bounded but non-trivial, and should be scoped as its own ticket rather than folded into feature work: set cdn_url/cdn_css_url to null (or to a .mjs URL) because MapLibre v6 is ESM-only — or deliberately pin a v5 UMD bundle to defer; re-publish published views with --force; remove any wire:ignore wrapped around <x-map>, since it now breaks the reactivity that is the point of the package; and re-validate UI against changed defaults (maps are lazy by default, :lazy="false" to opt out; popups/tooltips anchor with a 16px offset; cluster circles are smaller; height="full" now renders 100%). Treat the escaping and double-event fixes as the justification for scheduling it.
Compatibility and the exit hatch. Adopt through our own Blade partials (resources/views/maps/*.blade.php) that wrap the package components and own the data shapes. That single decision keeps a swap to raw MapLibre a contained rewrite of the partials rather than an architecture change, and it isolates the rest of the app from breaking component changes. Pin the minor version in composer.json, assert the map data shape in one integration test (the reactive contract — wire:key in loops, dispatch('map:update-*'), mapId targeting — is what breaks quietly), and add a coordinate-order assertion so [lng, lat] mistakes fail loudly.
Sequencing. Four gates, each independently valuable:
map:* events we actually consume (v1's double-delivery is a reminder that unthrottled event forwarding is a server-cost lever). Use <x-map blank> plus GeoJSON for internal polygon/coverage views to remove the basemap question entirely.blank), network/lane views (arcs + projection="globe"), and delivery progress playback (:progress + progress-model, which animates client-side with zero server requests) are now cheap enough to bundle into existing epics. Do not fund "a maps project."Maintenance. The real maintenance burden is ownership, not updates. Treat the pinned version as a source dependency: assign a named owner, watch releases, and run the map smoke test as part of any Livewire or Laravel major upgrade, because the reactive bridge is the surface most likely to break quietly. Budget a small recurring allowance (roughly a day per quarter) for version bumps, changelog review, and re-verification of the components we actually use. The source is small enough to read and patch — that is the mitigation for single-maintainer risk, and it should be stated explicitly in the adoption decision.
Support. No SLA, no vendor, community issue tracker only. Internal support runs through the wrapper partials and the bundled development guide; anyone debugging a map should start there rather than upstream. Escalation path is: read the component source, patch locally behind the pin, contribute upstream if broadly useful.
Scaling. Server-side cost is modest and predictable — the map renders client-side in WebGL, and Livewire dispatches are the only PHP round trips. Three levers matter: (1) event surface — forward only the map:* events we listen for, since every forwarded event is a request (v1's double-delivery was exactly this failure at 2x); (2) map density — lazy creation, queued pre-start commands, and content-hashed long-lived asset caching make dashboards with many maps viable, but WebGL contexts are a finite browser resource, so cap concurrent maps per page and prefer one map with layers over N maps; (3) external dependencies — tiles and routing are third-party latency. OSRM directions are session-cached and time out at 10s, so any UX assuming instant route rendering must be redesigned; tile host rate limits and outages are outside our control until we self-host.
Failure modes to instrument. CDN/asset load failure (blank map, no error surface); tile provider outage or rate limiting (grey map); OSRM timeout at 10s (route silently absent — needs an explicit empty/error state); WebGL context exhaustion after heavy wire:navigate use (the v1 leak is fixed in v2, but many-map pages still accumulate); coordinate-order inversion producing plausible-but-wrong pins (assert in tests); unescaped feature properties in popups rendering user-supplied strings (the exact class of defect v2 fixed — stay pinned at v2 or later); and commands dispatched to a lazy map that hasn't started yet (queued, but ordering assumptions should be tested).
Ramp-up. Low for anyone who writes Blade. A PHP developer ships a working map in an afternoon and a clustered, popup-driven, route-drawing view in a few days using the bundled skill guide and examples. The genuine learning curve is not the package — it is geospatial literacy: coordinate order, GeoJSON shape, projection and zoom semantics, and the judgement call between "this is a basemap problem" and "this is a data problem." Assign one engineer as the map owner to hold that context, keep the wrapper partials small and documented, and avoid letting map knowledge diffuse into a dozen half-owners.
How can I help you explore Laravel packages today?