Map
The simplest way to add an interactive map to your application.
Basic Usage
# The ng-map component handles MapLibre GL setup, theming, and provides context for child components.
import { Component } from '@angular/core';
import { MapComponent } from 'ng-mapcn';
@Component({
selector: 'app-basic-map',
standalone: true,
imports: [MapComponent],
template: `
<div class="h-[420px]">
<ng-map [center]="[-74.006, 40.7128]" [zoom]="12" />
</div>
`,
})
export class BasicMapComponent {}Controlled Mode
# Use the viewport input and viewportChange output to control the map's viewport externally. This is useful when you need to sync the map state with your application or respond to viewport changes.
import { Component, signal } from '@angular/core';
import { MapComponent, MapViewport } from 'ng-mapcn';
@Component({
selector: 'app-controlled-map',
standalone: true,
imports: [MapComponent],
template: `
<div class="h-[420px]">
<ng-map
[viewport]="viewport()"
(viewportChange)="viewport.set($event)"
/>
</div>
`,
})
export class ControlledMapComponent {
readonly viewport = signal<MapViewport>({
center: [-74.006, 40.713],
zoom: 8,
bearing: 0,
pitch: 0,
});
}Blank Basemap
# The blank input swaps the default street basemap for a transparent, tile-less canvas — perfect for data visualizations where you draw your own layers instead of showing streets and labels. Here, a ng-map-geojson layer renders world country borders on top of the transparent canvas.
import { Component } from '@angular/core';
import { MapComponent, MapGeoJSONComponent } from 'ng-mapcn';
@Component({
selector: 'app-blank-map',
standalone: true,
imports: [MapComponent, MapGeoJSONComponent],
template: `
<div class="h-[420px]">
<!-- "blank" is a transparent canvas. Add your own layers on top. -->
<ng-map [blank]="true" [center]="[0, 20]" [zoom]="1.2">
<ng-map-geojson [data]="worldGeoJsonUrl" />
</ng-map>
</div>
`,
})
export class BlankMapComponent {
readonly worldGeoJsonUrl = 'https://.../ne_110m_admin_0_countries.geojson';
}Custom Styles
# Use the styles input to provide custom map styles. This example uses free vector tiles from OpenFreeMap, an open-source project; the data comes from OpenStreetMap.
import { Component, computed, effect, signal } from '@angular/core';
import type { Map as MapLibreMap } from 'maplibre-gl';
import { MapComponent, MapStylesConfig } from 'ng-mapcn';
type StyleKey = 'default' | 'openstreetmap' | 'openstreetmap3d';
// Free vector tiles from OpenFreeMap, data from OpenStreetMap.
const STYLE_URLS: Record<StyleKey, MapStylesConfig | undefined> = {
default: undefined, // falls back to the built-in Carto light/dark styles
openstreetmap: {
light: 'https://tiles.openfreemap.org/styles/bright',
dark: 'https://tiles.openfreemap.org/styles/bright',
},
openstreetmap3d: {
light: 'https://tiles.openfreemap.org/styles/liberty',
dark: 'https://tiles.openfreemap.org/styles/liberty',
},
};
@Component({
selector: 'app-custom-style-map',
standalone: true,
imports: [MapComponent],
template: `
<div class="relative h-[420px] w-full">
<ng-map
[center]="[-0.1276, 51.5074]"
[zoom]="15"
[styles]="styles()"
(mapReady)="map.set($event)"
/>
<select
aria-label="Map style"
class="bg-background text-foreground absolute top-2 right-2 z-10 rounded-md border px-2 py-1 text-sm shadow"
[value]="styleKey()"
(change)="styleKey.set($any($event.target).value)"
>
<option value="default">Default (Carto)</option>
<option value="openstreetmap">OpenStreetMap</option>
<option value="openstreetmap3d">OpenStreetMap 3D</option>
</select>
</div>
`,
})
export class CustomStyleMapComponent {
readonly styleKey = signal<StyleKey>('default');
readonly styles = computed(() => STYLE_URLS[this.styleKey()]);
readonly map = signal<MapLibreMap | null>(null);
constructor() {
// "3D" simply tilts the camera — the liberty style already has buildings.
effect(() => {
const m = this.map();
if (!m) return;
m.easeTo({ pitch: this.styleKey() === 'openstreetmap3d' ? 60 : 0, duration: 500 });
});
}
}Fit to Bounds
# The fitBounds input fits the camera to the data of the map's children (markers, GeoJSON, routes, arcs, clusters, heatmaps) once the map is loaded, and again only when those bounds change. Pass bounds to fit explicit bounds, or call MapContextService.fitBounds() imperatively. It does not fight a controlled viewport. A draggable marker bound to signals refits continuously while dragged: pass explicit bounds for that case.
import { Component, signal } from '@angular/core';
import { MapComponent, MapMarkerComponent, MarkerContentComponent } from 'ng-mapcn';
@Component({
selector: 'app-fit-bounds-map',
standalone: true,
imports: [MapComponent, MapMarkerComponent, MarkerContentComponent],
template: `
<button (click)="addFarCity()">Add Tokyo</button>
<div class="h-[420px]">
<!-- Fits the markers once loaded, and again whenever their bounds change. -->
<ng-map [fitBounds]="{ padding: 64, maxZoom: 8 }">
@for (city of cities(); track city.name) {
<ng-map-marker [longitude]="city.lng" [latitude]="city.lat">
<ng-marker-content>
<div class="size-3 rounded-full bg-blue-500"></div>
</ng-marker-content>
</ng-map-marker>
}
</ng-map>
</div>
`,
})
export class FitBoundsMapComponent {
readonly cities = signal([
{ name: 'Paris', lng: 2.35, lat: 48.85 },
{ name: 'Berlin', lng: 13.4, lat: 52.52 },
{ name: 'Rome', lng: 12.5, lat: 41.9 },
]);
addFarCity() {
this.cities.update((c) => [...c, { name: 'Tokyo', lng: 139.69, lat: 35.69 }]);
}
}