How to Add Interactive Maps to Your Website (JavaScript Tutorial)
Tutorial

How to Add Interactive Maps to Your Website (JavaScript Tutorial)

Learn how to add interactive maps to your website with JavaScript, step-by-step. Covers markers, popups, geocoding search, and React/Next.js integration using the MapAtlas API.

MapAtlas Team7 min read
#maps#javascript#api#react#next.js#geocoding

Adding interactive maps to your website has become a baseline expectation. Logistics dashboards track live vehicle positions, real estate apps surface neighbourhoods, food platforms draw delivery zones. This step-by-step JavaScript tutorial shows you exactly how to do it.

We cover the full stack: rendering your first map, adding markers and popups, wiring up address search with the Geocoding API, and integrating with React and Next.js.

By the end you will have:

  • A live vector map initialised with your API key
  • Interactive markers with clickable popup content
  • Address search powered by the Geocoding API
  • A reusable map component ready for React and Next.js
  • Production performance tips you can apply immediately

What You Will Need

  • A MapAtlas API key (sign up free, no credit card required)
  • A JavaScript project: plain HTML, React, Vue, or Svelte all work
  • Node.js 18+ if you are using npm

Your API key authenticates all MapAtlas services (tiles, geocoding, routing), so there is only one credential to manage.

Step 1: Install the SDK

Add the MapAtlas SDK to your project:

npm install @mapmetrics/mapmetrics-gl

For plain HTML pages, paste the CDN links into your <head>:

<link
  rel="stylesheet"
  href="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css"
/>
<script src="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.js"></script>
Note:

The CSS import is required. Without it the map controls and popups render without any styling, which will look broken in production.

Step 2: Add a Map Container

Place a <div> wherever the map should appear. The SDK fills the exact dimensions of this element, so always set an explicit height:

<div id="map" style="width: 100%; height: 500px;"></div>
Warning:

Forgetting to set a height is the most common setup mistake. If the container has height: 0, the map renders but is invisible. Always give it a fixed pixel or vh value.

Step 3: Render Your First Map

Initialise the map with your API key, a centre coordinate, and a zoom level:

import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';

const map = new mapmetricsgl.Map({
  container: 'map',
  style: 'https://tiles.mapatlas.eu/styles/basic/style.json?key=YOUR_API_KEY',
  center: [4.9041, 52.3676], // [longitude, latitude]
  zoom: 12,
});

Open the page and you will see a fully interactive vector map. Drag, scroll, and pinch to navigate. Tiles are fetched on demand from MapAtlas servers.

Picking a Map Style

Swap the path segment in the style URL to change the visual theme:

StyleURLWorks best for
Basic/styles/basic/style.jsonGeneral purpose apps
Bright/styles/bright/style.jsonData visualisation
Dark/styles/dark/style.jsonDashboards, night mode
Tip:

Use the Dark style for admin dashboards and tools used primarily at night. It reduces eye strain and makes data layers like heatmaps and route lines pop.

Step 4: Add Markers and Popups

A map without markers is just a background image. Here is how to place an interactive pin and attach content to it:

Single Marker with Popup

const popup = new mapmetricsgl.Popup().setHTML(`
  <strong>Amsterdam Central</strong>
  <p>Stationsplein, 1012 AB Amsterdam</p>
`);

new mapmetricsgl.Marker({ color: '#97C70A' })
  .setLngLat([4.9001, 52.3791])
  .setPopup(popup)
  .addTo(map);

Click the marker to open the popup. You can put any HTML inside: addresses, images, action buttons, whatever your UI needs.

Multiple Markers from an Array

Most apps need more than one pin. Loop over your data and call new Marker() for each location:

const locations = [
  { name: 'Amsterdam', coords: [4.9041, 52.3676] },
  { name: 'Rotterdam', coords: [4.4777, 51.9244] },
  { name: 'Utrecht',   coords: [5.1214, 52.0907] },
];

locations.forEach(({ name, coords }) => {
  const popup = new mapmetricsgl.Popup().setHTML(`<strong>${name}</strong>`);
  new mapmetricsgl.Marker({ color: '#97C70A' })
    .setLngLat(coords)
    .setPopup(popup)
    .addTo(map);
});
Tip:

If you have more than 100 markers, enable clustering so the map stays responsive. The SDK supports GeoJSON source clustering out of the box. See the docs for the configuration.

Step 5: Add Address Search with the Geocoding API

The Geocoding API converts a text query (a street address, a city name, or a place) into coordinates you can pan to, mark, or pass to the routing API.

async function searchAddress(query) {
  const url = new URL('https://api.mapatlas.eu/geocoding/v1/search');
  url.searchParams.set('text', query);
  url.searchParams.set('key', 'YOUR_API_KEY');

  const res  = await fetch(url);
  const data = await res.json();

  if (!data.features.length) return;

  const [lng, lat] = data.features[0].geometry.coordinates;
  const label      = data.features[0].properties.label;

  // Fly to the result and drop a marker
  map.flyTo({ center: [lng, lat], zoom: 14 });

  new mapmetricsgl.Marker({ color: '#97C70A' })
    .setLngLat([lng, lat])
    .setPopup(new mapmetricsgl.Popup().setHTML(`<strong>${label}</strong>`))
    .addTo(map);
}

// Example usage
searchAddress('Rijksmuseum, Amsterdam');

Results are returned as GeoJSON features, which you can plug directly into any GeoJSON-aware visualisation, data table, or routing request.

Tip:

Wire up an <input> element to searchAddress on the input or change event and you have a complete live search bar in under 30 lines of code.

Integrating with React

A Reusable Map Component

Wrap the map in a useEffect so it initialises after the DOM node is mounted, and clean up on unmount to avoid memory leaks:

import { useEffect, useRef } from 'react';
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';

export function MapAtlasMap({
  apiKey,
  center = [4.9041, 52.3676],
  zoom   = 12,
}) {
  const containerRef = useRef(null);

  useEffect(() => {
    const map = new mapmetricsgl.Map({
      container: containerRef.current,
      style: `https://tiles.mapatlas.eu/styles/basic/style.json?key=${apiKey}`,
      center,
      zoom,
    });

    return () => map.remove(); // prevent memory leaks on re-render
  }, [apiKey]);

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

Drop it anywhere in your component tree:

<MapAtlasMap
  apiKey={process.env.NEXT_PUBLIC_MAPATLAS_KEY}
  center={[4.9041, 52.3676]}
  zoom={13}
/>

Using with Next.js

The map SDK uses browser APIs (window, document) that are not available during server-side rendering. In Next.js, import the component dynamically with ssr: false:

import dynamic from 'next/dynamic';

const MapAtlasMap = dynamic(
  () => import('./MapAtlasMap').then(m => m.MapAtlasMap),
  {
    ssr: false,
    loading: () => (
      <div style={{ height: 500, background: '#f0f1f3', borderRadius: 12 }} />
    ),
  }
);
Note:

The loading skeleton keeps your layout stable while the bundle downloads, preventing layout shift when the map appears. Always provide a placeholder that matches the map dimensions.

Performance Best Practices

A few habits that separate a smooth, production-ready map from one that struggles at scale:

  • Lazy-load maps below the fold: use IntersectionObserver to initialise the map only when the container scrolls into view. Saves roughly 200 KB of JavaScript on initial load.
  • Use vector tiles, not raster: vector tiles scale cleanly to any screen density, load faster, and can be styled client-side. The MapAtlas style URLs serve vector tiles by default.
  • Cluster large datasets: once you exceed 100 to 200 markers, enable clustering. Unclustered markers on a zoomed-out view cause severe rendering slowdowns.
  • Store your API key server-side: never commit your key to a public repo. Use environment variables (NEXT_PUBLIC_MAPATLAS_KEY in Next.js, or a backend proxy for anything sensitive).
  • Set maxBounds: if your users only care about one region, restrict the map viewport so tiles outside that area are never requested.

What to Build Next

You now have a map that renders, places markers, searches addresses, and integrates cleanly with React. The next natural steps:

  • Routing API: request turn-by-turn directions between two coordinates. Returns a route polyline plus distance and estimated travel time.
  • Isochrone API: generate a polygon covering every point reachable within N minutes of travel. Used for delivery zones, coverage maps, and property catchment areas.
  • Matrix API: calculate travel times and distances between multiple origins and destinations in a single request. Essential for fleet dispatch and logistics optimisation.

Full SDK reference, style documentation, and API guides are at docs.mapatlas.xyz.

Frequently Asked Questions

Can I add interactive maps to my website for free?

Yes. MapAtlas includes a free tier with no credit card required at sign-up. It covers vector tile rendering, the Geocoding API, and the routing API, enough for most development and small production use cases.

How do I embed a map in a React or Next.js app?

Wrap the map initialisation in a useEffect hook so it runs after the DOM mounts. In Next.js, use dynamic import with ssr: false to prevent server-side rendering errors. The guide above includes copy-paste examples for both frameworks.

What is a vector tile map and why is it better than raster?

Vector tiles are mathematical descriptions of map features (roads, buildings, labels) rather than pre-rendered images. They scale sharply to any screen resolution, load faster, and can be restyled client-side without additional server requests, making them the default choice for modern web map applications.

How many markers can I add before map performance degrades?

Performance typically degrades noticeably beyond 100 to 200 markers on a single view at full zoom-out. The solution is clustering: the MapAtlas SDK supports GeoJSON source clustering out of the box, which groups nearby markers at low zoom levels and keeps rendering smooth regardless of dataset size.

Do I need GIS knowledge to add a map to my app?

No. The MapAtlas JavaScript SDK is designed for web developers without GIS expertise. You initialise the map with coordinates and a zoom level, add markers with longitude/latitude pairs, and call the Geocoding API with plain text queries. No spatial database or GIS tooling required.

Found this useful? Share it.

Back to Blog