Skip to content

Map

The map package renders interactive maps from server-defined features. The public PHP and wire APIs use coordinates, providers, and typed features rather than provider concepts. OpenStreetMap and Google Maps are the built-in providers; another package can add its own provider without changing page definitions.

The package supports markers and routes. A marker can display any Lattice component schema in a popup, including links, forms, and registered actions. A route draws a server-supplied path as a line; computing that path is the application’s concern. Polygons, editing, geocoding, and clustering are outside the API.

Terminal window
composer require lattice-php/map

Composer is the only install. The lattice() Vite plugin discovers the package through Composer and adds only a small entry to the app bundle: the map component and its precompiled renderers — with Leaflet bundled inside, while React and the Lattice runtime remain shared with the host app — load on demand the first time a page renders a map, so pages without a map never pay for Leaflet. For a no-build app, run php artisan lattice:assets after installation; it publishes the same self-contained renderer as a single standalone plugin.

In an app with a build step, import the map stylesheet in your application CSS — it positions the Leaflet panes and themes the controls, markers, and popups with the Lattice design tokens:

@import "@lattice-php/map/css";
Map::make('offices')
->height(360)
->markers([
Marker::make('berlin')
->position(52.5200, 13.4050)
->label('Berlin office')
->popup([
Stack::make()->gap(Gap::Small)->schema([
Heading::make('Berlin office', 3),
Text::make('Alexanderplatz 1'),
]),
])
->open(),
Marker::make('hamburg')
->position(53.5511, 9.9937)
->label('Hamburg office')
->color(ColorName::Warning),
]);

->open() marks one popup for automatic opening after the map initializes. Only one marker may be open initially, and marker IDs must be unique. Labels supply each marker’s accessible name even when the popup schema renders different text.

->icon() replaces the pin’s dot with any Lattice icon (a Lattice\Ui\Enums\Icon case, or the sprite id of an app-registered icon), and ->color() tints the pin with a named Lattice color or a CSS color. Markers without either keep the primary pin with the plain dot.

Without an explicit center, one marker opens at a practical street-level zoom and several markers are fitted into view. An empty map shows the world. Override the viewport and controls when needed:

Map::make('service-area')
->center(latitude: 52.52, longitude: 13.405)
->zoom(11)
->height(520)
->scrollZoom()
->navigationControls(false)
->markers($markers);

Scroll-wheel zoom is disabled by default so a map embedded in a page does not capture normal page scrolling — holding Cmd/Ctrl while scrolling (or a trackpad pinch) still zooms the map; ->scrollZoom() makes the plain wheel zoom too. Zoom values must fit both the general 0–24 range and the active provider’s range.

When a default zoom is set with ->zoom(), the map shows a reset control below the zoom buttons that returns the viewport to its initial center and zoom after panning or zooming away. ->navigationControls(false) hides it together with the zoom buttons.

->routes() draws server-supplied paths as lines on the map. A route takes a list of coordinates — [latitude, longitude] pairs or CoordinateData instances — and optional styling. Routes take part in the automatic viewport fit exactly like markers:

Map::make('commute')
->height(360)
->routes([
Route::make('berlin-potsdam')
->path([
[52.5200, 13.4050],
[52.5063, 13.3320],
[52.4581, 13.2107],
[52.3989, 13.0657],
])
->color(ColorName::Info)
->weight(4),
])
->markers([
Marker::make('berlin')
->position(52.5200, 13.4050)
->label('Berlin office'),
Marker::make('potsdam')
->position(52.3989, 13.0657)
->label('Potsdam office'),
]);

A route needs at least two points and an id that is unique across all features on the map, markers included. ->color() accepts the same named Lattice colors and CSS colors as markers; without one the route uses the primary color. ->weight() sets the stroke width in pixels.

The built-in provider uses the public OpenStreetMap tile service and includes the required attribution. This default is convenient for development and modest interactive use, but the public service has no SLA and is not a free general-purpose CDN. Production applications must follow the OpenStreetMap tile usage policy, keep attribution visible, and use a suitable commercial or self-hosted tile service when traffic or availability requirements demand it.

Override the tile endpoint without changing map definitions:

In config/map.php:

return [
'default_provider' => 'openstreetmap',
'providers' => [
'openstreetmap' => [
'tile_url' => env('MAP_TILE_URL', 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'),
'attribution' => '© OpenStreetMap contributors',
'minimum_zoom' => 1,
'maximum_zoom' => 19,
],
],
];

The Google Maps provider ships with the package and activates once an API key is configured:

GOOGLE_MAPS_API_KEY=your-browser-key

Select it per map with Map::make()->provider('googlemaps'), or make it the app-wide default with 'default_provider' => 'googlemaps' in config/map.php. Markers, popups, routes, viewport, and controls behave exactly as on OpenStreetMap; the provider supports zoom levels 0–22. Without a configured key the provider is not registered, so unconfigured apps keep working with OpenStreetMap alone.

The renderer loads the Google Maps JavaScript API from maps.googleapis.com at runtime — nothing Google-specific is bundled. If your app sets a Content Security Policy, allow that origin for scripts and its tile hosts for images.

Markers render as Advanced Markers, which require a map id. The provider defaults to Google’s DEMO_MAP_ID:

GOOGLE_MAPS_MAP_ID=your-map-id

A provider has one server and one client registration. The server implementation returns its stable name, provider-specific browser options, and supported zoom range:

use Lattice\Map\Contracts\MapProvider;
use Lattice\Map\MapProviderData;
use Lattice\Map\MapProviderRegistry;
final class MapboxProvider implements MapProvider
{
public function data(): MapProviderData
{
return new MapProviderData(
name: 'mapbox',
options: ['accessToken' => config('services.mapbox.token')],
minimumZoom: 0,
maximumZoom: 22,
);
}
}
app(MapProviderRegistry::class)->register(app(MapboxProvider::class));

Its Lattice plugin registers a React component under the matching map.providers key:

import type { Plugin } from "@lattice-php/core";
import MapboxMap from "./mapbox-map";
export default {
name: "acme/mapbox",
extensions: {
"map.providers": {
mapbox: MapboxMap,
},
},
} satisfies Plugin;

Applications then select it with Map::make()->provider('mapbox'). The map component keeps the same markers, routes, popup schemas, viewport, and controls contract; only the provider options and renderer change. The isMarkerFeature and isRouteFeature guards exported from @lattice-php/map narrow the wire features to the type a renderer is drawing.

The report measures the precompiled map plugin. It includes Leaflet but externalizes React and the framework runtime, so it represents the package’s additional JavaScript — loaded only when a page actually renders a map.

50.5 KB gzipped · 198.7 KB raw JavaScript

DependencyRawGzipShare
@lattice-php/map12.0 KB4.1 KB8.1%
leaflet165.8 KB44.8 KB88.7%
Bundler runtime20.8 KB1.6 KB3.2%

Emitted JavaScript files

FileRawGzip
plugin.js198.7 KB50.5 KB

Open the full interactive treemap ↗ · Generated by Sonda 0.14.0 on Sat, 05 Sep 2026 21:22:16 GMT.