Project setup

Install the SDK, set your API key and wire the map into your framework.

Prerequisites and requirements

The Services bundle also runs in Node.js and React Native. The Map bundle runs in the browser, in any framework (React, Vue, Angular or none).

Obtaining your API key

  1. Navigate to my.tomtom.com
  2. Create an account or sign in to your existing MyTomTom account
  3. Go to Products, find Maps SDK for JavaScript in the list and click Start now
  4. Click Create new free SDK key
  5. Configure key permissions for the APIs you plan to use

Before you ship, decide where this key will live. Anyone who opens the page can read a key you use from the browser, but not one you use from Node.js. See Where your API key runs, and TomTom's API key management best practices for key restrictions, rotation and monitoring.

AI coding agent skill

Using an AI coding agent? Install the tomtom-maps-sdk-js skill so it writes against the real API. AI coding with the SDK has the install line and the topics it covers.

Installing dependencies

One package, @tomtom-org/maps-sdk, carries all three bundles: /core, /services and /map.

SDK installation

npm install @tomtom-org/maps-sdk

Installing peer dependencies

npm (v7+) and Yarn install the peer dependencies with the SDK:

npm install @tomtom-org/maps-sdk
# or
yarn add @tomtom-org/maps-sdk

pnpm installs them once you ask it to:

# One-time setup
echo "auto-install-peers=true" >> .npmrc

pnpm add @tomtom-org/maps-sdk

Or install them yourself:

pnpm add @tomtom-org/maps-sdk @turf/turf culori lodash-es maplibre-gl zod

Peer dependency requirements by bundle

Peer dependency Version License Used by
lodash-es 4 MIT Core, Services, Map
@turf/turf 7 MIT Core, Services, Map: geospatial calculations
zod 4 MIT Services: request validation
maplibre-gl 6 BSD-3-Clause Map: rendering (About MapLibre GL JS)
culori 4 MIT Map: colour parsing, conversion and contrast

The package declares all five as peer dependencies, so install all five even when you import one bundle.

Peer dependency licenses

The SDK doesn't bundle its peer dependencies. Your package manager installs them, and your app bundles them under their own licenses. THIRD_PARTY.txt, at the root of the @tomtom-org/maps-sdk package, reproduces each license and copyright notice in full. The public repository holds the latest release's copy. Each plugin package ships a THIRD_PARTY.txt of its own for its peer dependencies.

Vite projects

MapLibre GL JS v6 runs its web worker from a separate file, maplibre-gl-worker.mjs, which it locates at runtime. Neither the Vite dev server nor vite build emits that file on its own. Without it the map stays blank, and the console logs Worker failed to load.

TomTomMap takes care of it on Vite 8, and in vite build on Vite 7, with no Vite configuration. The SDK hands Vite a worker entry that imports your own maplibre-gl worker. It then registers the URL Vite gives it with MapLibre before creating the map. Your app's origin serves the worker, with no third-party request, and the worker always matches your MapLibre version. After vite build, dist/assets holds a maplibre-gl-worker-*.js file next to your bundle.

The Vite 7 dev server is the exception. Its pre-bundler moves the SDK into node_modules/.vite/deps/, where the worker entry doesn't exist. Upgrade to Vite 8, or keep the SDK out of pre-bundling and list its peer dependencies, which Vite then pre-bundles on their own:

// vite.config.ts, Vite 7
import { defineConfig } from 'vite';

export default defineConfig({
    optimizeDeps: { exclude: ['@tomtom-org/maps-sdk'], include: ['@turf/turf', 'culori/fn', 'lodash-es', 'zod'] },
});

If you use the Landmarks 3D plugin, add @tomtom-org/maps-sdk-plugin-landmarks-3d to exclude too. Its texture transcoder files sit beside it, where the pre-bundler can't follow.

TomTomMap leaves MapLibre's own worker lookup in place when:

import { TomTomMap } from '@tomtom-org/maps-sdk/map';

const map = new TomTomMap({ registerMapLibreWorker: false, mapLibre: { container: 'map' } });

TypeScript support

The SDK's own types ship with the package, so they need no @types package:

import { TomTomConfig, type Language } from '@tomtom-org/maps-sdk/core';
import { TomTomMap } from '@tomtom-org/maps-sdk/map';

Places, routes and geometries are GeoJSON. To name a GeoJSON type such as Position, Feature or FeatureCollection in your own code, import it from geojson:

import type { Position } from 'geojson';
import type { BBox } from '@tomtom-org/maps-sdk/core';

Import BBox from the SDK instead. The SDK's BBox has exactly four numbers, which its bounding box utilities expect, while GeoJSON's also allows six.

npm resolves geojson through the copy of @types/geojson that maplibre-gl installs. pnpm doesn't let your code import another package's dependencies, so install it yourself:

pnpm add -D @types/geojson

SDK common configuration

Set your API key once at startup with TomTomConfig. The map and every service call read it from there:

import { TomTomConfig } from '@tomtom-org/maps-sdk/core';

TomTomConfig.instance.put({
    apiKey: process.env.TOMTOM_API_KEY,
    language: 'en-GB',
});

Multiple API keys

A service call or a map can override the global key:

import { TomTomConfig } from '@tomtom-org/maps-sdk/core';
import { discoverPlaces } from '@tomtom-org/maps-sdk/services';
import { TomTomMap } from '@tomtom-org/maps-sdk/map';

TomTomConfig.instance.put({ apiKey: process.env.TOMTOM_DEFAULT_API_KEY });

const results = await discoverPlaces({
    query: 'restaurants',
    geoBias: { position: [4.9041, 52.3676] },
    apiKey: process.env.TOMTOM_PREMIUM_API_KEY, // this call only
});

const map = new TomTomMap({
    mapLibre: { container: 'map' },
    apiKey: process.env.TOMTOM_MAP_API_KEY, // this map only
});

Framework-specific setup

React

// src/config/tomtom.js
import { TomTomConfig } from '@tomtom-org/maps-sdk/core';

TomTomConfig.instance.put({
    apiKey: process.env.REACT_APP_TOMTOM_API_KEY,
    language: 'en-US'
});

// src/components/Map.jsx
import { TomTomMap } from '@tomtom-org/maps-sdk/map';
import { useEffect, useRef } from 'react';

export function MapComponent() {
    const mapRef = useRef();

    useEffect(() => {
        const map = new TomTomMap({
            mapLibre: {
                container: mapRef.current
            }
        });

        return () => map.mapLibreMap.remove();
    }, []);

    return <div ref={mapRef} style={{ width: '100%', height: '400px' }} />;
}

Vue

// src/plugins/tomtom.js
import { TomTomConfig } from '@tomtom-org/maps-sdk/core';

TomTomConfig.instance.put({
    apiKey: process.env.VUE_APP_TOMTOM_API_KEY,
    language: 'en-US'
});

// src/components/MapComponent.vue
<template>
    <div ref="mapContainer" class="map-container"></div>
</template>

<script>
import { TomTomMap } from '@tomtom-org/maps-sdk/map';

export default {
    mounted() {
        this.map = new TomTomMap({
            mapLibre: {
                container: this.$refs.mapContainer
            }
        });
    },
    beforeUnmount() {
        this.map?.mapLibreMap.remove();
    }
};
</script>

Node.js

// src/config/tomtom.js
import { TomTomConfig } from '@tomtom-org/maps-sdk/core';

TomTomConfig.instance.put({
    apiKey: process.env.TOMTOM_API_KEY,
    language: 'en-US'
});

// src/services/geocoding.js
import { geocode } from '@tomtom-org/maps-sdk/services';

export async function geocodeAddress(query) {
    return await geocode({
        query
    });
}

Next steps