Skip to content

Use Maptoolkit Maps in Angular

A map in Angular is a standalone component with a viewChild for the container, the map created in ngAfterViewInit and removed in ngOnDestroy. The Angular-specific issue is the build budget: the map library and Angular together are larger than the default limit for the initial bundle.

The component

import { Component, ElementRef, AfterViewInit, OnDestroy, viewChild } from "@angular/core";
import * as maplibregl from "maplibre-gl";

@Component({
  selector: "app-maptoolkit-map",
  template: `<div #container class="map"></div>`,
  styles: `.map { width: 100%; height: 400px; }`,
})
export class MaptoolkitMap implements AfterViewInit, OnDestroy {
  private container = viewChild.required<ElementRef<HTMLDivElement>>("container");
  private map?: maplibregl.Map;

  ngAfterViewInit() {
    this.map = new maplibregl.Map({
      container: this.container().nativeElement,
      attributionControl: { compact: false },
      style: "https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=YOUR_API_KEY",
      center: [11.4041, 47.2692],
      zoom: 12,
    });
  }

  ngOnDestroy() {
    this.map?.remove();
  }
}
  • The map is created in ngAfterViewInit, the first hook in which the container element exists.
  • remove() in ngOnDestroy frees the WebGL context. Browsers allow only a handful of live contexts, and a map that is never removed keeps its context after the route changes.
  • The container needs a height, or the map initializes into a zero-pixel element with no error.

The stylesheet

Add the MapLibre GL JS stylesheet to the styles array of the build target in angular.json, so it is loaded once for the whole app:

"styles": [
  "node_modules/maplibre-gl/dist/maplibre-gl.css",
  "src/styles.css"
]

Keep the map out of the initial bundle

A new Angular project fails its production build as soon as a component imports MapLibre GL JS directly, with bundle initial exceeded maximum budget. MapLibre GL JS is about 0.9 MB minified (1.1 MB for version 6), and together with Angular itself that is over the default 1 MB limit for the initial bundle. Wrap the map in a @defer block where you use it:

@defer {
  <app-maptoolkit-map />
} @placeholder {
  <div style="height: 400px"></div>
}

Angular then builds the map component into its own chunk and loads it after the page. The placeholder keeps the page from jumping when the map appears; give it the map’s height.

Raising the budget in angular.json also makes the build pass, but then the map library has to download before the page first renders.

Next steps