Skip to content
MapLibre GL JS

Use Maptoolkit with ngx-maplibre-gl in Angular

ngx-maplibre-gl wraps MapLibre GL JS in Angular components. A Maptoolkit style URL goes into the mapStyle input; the style names its own tile sources and carries its own attribution.

Install

npm install @maplibre/ngx-maplibre-gl maplibre-gl

Version 22 requires Angular 22 and MapLibre GL JS 6.

The component

import { Component } from "@angular/core";
import { MapComponent, MarkerComponent, ControlComponent, NavigationControlDirective } from "@maplibre/ngx-maplibre-gl";

@Component({
  selector: "app-maplibre-map",
  imports: [MapComponent, MarkerComponent, ControlComponent, NavigationControlDirective],
  template: `
    <mgl-map
      class="map"
      [mapStyle]="style"
      [center]="[11.4041, 47.2692]"
      [zoom]="12"
      [attributionControl]="{ compact: false }"
    >
      <mgl-control mglNavigation position="top-right" />
      <mgl-marker [lngLat]="[11.4041, 47.2692]" />
    </mgl-map>
  `,
  styles: `.map { height: 400px; }`,
})
export class MaplibreMap {
  style = "https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=YOUR_API_KEY";
}
  • The style input is mapStyle, and zoom is a number. Older tutorials show [style] and [zoom]="[12]"; with version 22 both fail to compile.
  • <mgl-control mglNavigation> needs two imports, ControlComponent for the element and NavigationControlDirective for the attribute. With only the directive, the page throws NG0201: No provider for ControlComponent found.
  • attributionControl with compact: false keeps the credit visible. By default MapLibre collapses it into an icon as soon as the user interacts with the map.
  • mgl-map needs a height, or the map renders nothing and reports no error.

Serve the MapLibre worker

MapLibre 6 loads its web worker from a separate file at runtime, which an Angular build does not emit. Without the setup below, the map stays blank and never finishes loading. ngx-maplibre-gl documents the fix: copy the worker into the build output, then tell the library where it is.

In angular.json, add both files to assets, together, because the worker imports the second one from its own directory. Add MapLibre’s stylesheet to styles while you are there:

"styles": [
  "node_modules/maplibre-gl/dist/maplibre-gl.css",
  "src/styles.css"
],
"assets": [
  { "glob": "**/*", "input": "public" },
  { "glob": "maplibre-gl-worker.mjs", "input": "node_modules/maplibre-gl/dist", "output": "/" },
  { "glob": "maplibre-gl-shared.mjs", "input": "node_modules/maplibre-gl/dist", "output": "/" }
]

In app.config.ts:

import { ApplicationConfig } from "@angular/core";
import { provideMaplibreWorker } from "@maplibre/ngx-maplibre-gl/config";

export const appConfig: ApplicationConfig = {
  providers: [provideMaplibreWorker("maplibre-gl-worker.mjs")],
};

The provider applies the URL right before the first map is created, relative to the page’s base URL, so it also works when the app is deployed under a sub-path.

Keep the map out of the initial bundle

The map component comes to about 1.08 MB, over Angular’s default 1 MB budget for the initial bundle, so a direct import fails the production build. Wrap the map in @defer where you use it:

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

The Maps JS Angular page has the measurements and the trade-off against raising the budget.

Next steps

Sources and layers are components as well, mgl-geojson-source and mgl-layer, and mgl-layer takes the same paint and layout objects as MapLibre’s addLayer(). The per-API MapLibre examples carry over directly: a route from the Routing API, or relief from Terrain Tiles.