Skip to main content
WireKit
Copy for LLM

Map

An interactive map for geo surfaces — fleet tracking, real-estate search, field-service dispatch, store locators. Like chart and the rich-text editor, map is an adapter: WireKit ships the themed chrome, the declarative API, the Alpine glue, and — most importantly — an accessible marker list that is always present. The heavy map engine and the map tiles are your app's concern, so the engine (~200 KB) never enters WireKit's bundle.

map needs a peer dependency — a map engine loaded on the page. Pick one: MapLibre GL (the default — vector tiles, fully themeable) or Leaflet (lightweight raster tiles). See Choosing a Map Engine below, then the MapLibre GL setup guide. Without an engine the component degrades to the marker list plus a placeholder, so your data stays reachable.

Markers

Pass a center, a zoom, and an array of markers — each marker appears in the accessible list alongside the map. Selecting one (from the list or by clicking its pin on the map) pans the map, highlights the matching list entry, and emits marker-click. A marker's intent colors both its list dot and — once a map engine is loaded — the map pin itself, while the label and coordinates always carry the meaning, so the map never depends on color alone. An optional body line (an address, opening hours) renders under the label in the list, as the second line of the pin's tooltip, and inside the row's accessible name.

Both engines render standard on-canvas zoom controls (zoom in / out), so a reader can scale the map without a scroll-wheel gesture.

This overview stays engine-agnostic; the live previews live on the engine guides below, where a real map (with real tiles) renders. Pick an engine, then follow its guide.

Choosing a Map Engine

The same <x-wirekit::map> API drives either engine — you choose with the provider prop and supply the engine's library on the page. The marker list, the accessible API, and the theming are identical; only the tiles and the rendering differ.

Engine provider Tiles License Best for
MapLibre GL maplibre (default) Vector, fully styleable BSD-3-Clause Custom-styled, themeable interactive maps
Leaflet leaflet Raster (you pick the tile source) BSD-2-Clause A small footprint and any raster tile provider

Either engine is your app's dependency, not WireKit's — so load it only on the routes that render a map, never globally, or it weighs down every page. MapLibre GL in particular is large (~750 KB gzipped); the MapLibre GL guide shows the route-scoped loading pattern.

Each engine has a dedicated guide: the MapLibre GL guide (install, styling, dark-mode tiles, real-world patterns) and the Leaflet & OpenStreetMap guide (install, raster tiles, the free OSM tile source with its attribution requirement, usage policy, and license — engine and tile source together on one page).

Engine vs. tile source — which page do I read? They are different layers, and a live map needs both. The engine is the JavaScript library that draws the map (MapLibre or Leaflet — that's the provider). The tile source is where the actual map imagery comes from (OpenStreetMap, a commercial vector style, your own server — that's the styleUrl). So Leaflet and OpenStreetMap are not alternatives to each other: Leaflet is one engine that can render OpenStreetMap tiles — which is why the Leaflet & OpenStreetMap guide covers both layers on one page: the engine setup first, then that tile source's attribution requirement, usage policy, and license.

Whichever engine you choose, the tiles carry their own license and attribution terms — they are separate from the engine's own (BSD) license. Always display the tile provider's required attribution, and check its usage policy before shipping at scale. The MapLibre GL guide covers this for each tile source.

Livewire & Realtime

Handle marker clicks server-side, and stream live positions in from Laravel Echo by calling the component's upsertMarker method:

{{-- 1. Open a detail panel when a marker is clicked --}}
<x-wirekit::map
    :center="$center"
    :markers="$this->vehicles"
    x-on:marker-click="$wire.selectVehicle($event.detail.id)"
/>
// 2. Shape markers for the map
public function getVehiclesProperty(): array
{
    return Vehicle::all()->map(fn ($v) => [
        'id' => $v->id,
        'lat' => $v->latitude,
        'lng' => $v->longitude,
        'label' => "{$v->name} — {$v->status}",
        'intent' => match ($v->status) {
            'active' => 'success', 'delayed' => 'warning', default => 'danger',
        },
    ])->all();
}
// 3. Push live position updates (optimistic; the list + map both update)
Echo.private('fleet').listen('VehicleMoved', (e) => {
    window.dispatchEvent(new CustomEvent('vehicle-moved', { detail: e }));
});
// Wire it on the element: x-on:vehicle-moved.window="upsertMarker($event.detail)"

Props

Prop Type Default Description
center array [0, 0] [lat, lng] initial center
zoom int 2 Initial zoom level
markers array [] [{id, lat, lng, label, body?, image?, tooltip?, intent?}] — the pin bubble follows the data shape: label alone is a text-only line, body adds a muted detail row (address, opening hours; also shown in the list), image adds a photo card above the text, and tooltip: 'image' makes the bubble photo-only (the label still names the pin for screen readers and the list)
provider string 'maplibre' maplibre · leaflet (peer dependency)
styleUrl string|null null Tile / style URL (provider-specific)
attribution string|null null Tile attribution shown in Leaflet's attribution control (e.g. © OpenStreetMap contributors) — required by some tile sources
height string '24rem' Map canvas height (CSS length)
ariaLabel string 'Map' Accessible name for the map region
listLabel string 'Locations' Heading for the marker list
highlight string 'ring' Selected-row highlight: ring (inset frame) · fill (tinted tile background)
highlightColor string 'accent' Highlight color: accent · success · warning · danger · neutral
list bool true Show the marker-list sidebar; false renders a map-only surface (the list stays sr-only for accessibility)
scope string|null null Scoped personalization name

Accessibility

The marker list is the screen-reader experience — always keep it visible (or at least in the DOM). The visual map is the enhancement on top.

  • A map is opaque to assistive tech, so the marker list is mandatory and always rendered. Each entry is a focusable button labeled with the marker name and its coordinates, so the data is reachable without seeing the map.
  • The map canvas carries role="application" with an aria-label and is keyboard-focusable; the list region is a labeled, keyboard-reachable scroll region (WCAG 2.1.1).
  • Marker status is conveyed by the label text, never the dot color alone.
  • Pan/fly animations honor prefers-reduced-motion (the map jumps instead).

Keyboard Interaction

Key Action
Tab Move into the map canvas and through the marker list
Enter / Space Select a marker (pans the map, emits marker-click)

Design Tokens

Element Token
Canvas backdrop --color-wk-bg-muted
List surface --color-wk-bg-elevated
Marker dot (accent/success/warning/danger) --color-wk-accent / --color-wk-success / --color-wk-warning / --color-wk-danger
Borders --color-wk-border
Focus ring --color-wk-ring

Customization

Set the default provider and zoom via config/wirekit.php:

'components' => [
    'map' => [
        'provider' => 'leaflet',
        'zoom' => 11,
    ],
],

Further Reading