Every logistics dashboard, real estate listing, and food delivery app has one thing in common: a map. If your website doesn't have one yet, you're leaving context on the table. Context that helps users understand location, proximity, and spatial relationships at a glance.
This tutorial walks you through adding a fully interactive map to any JavaScript project. You'll start with a rendered map, layer on markers and popups, wire up address search with the Geocoding API, and finish with a production-ready React component that drops cleanly into Next.js.
Here's what you'll build by the end:
- A live vector map authenticated with your API key
- Clickable markers with custom popup content
- A geocoding-powered address search function
- A reusable React component with proper cleanup and Next.js SSR handling
- A checklist of performance optimizations for production
Prerequisites
Before you start, make sure you have:
- A MapAtlas API key (sign up free, no credit card required). This single key authenticates every MapAtlas service: tiles, geocoding, and routing.
- A JavaScript project. Plain HTML, React, Vue, or Svelte all work.
- Node.js 18+ if you're installing via npm.
Step 1: Install the MapAtlas SDK
Pull the SDK into your project with npm:
npm install @mapmetrics/mapmetrics-gl
If you're working with a plain HTML page instead, drop 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>
Don't skip the CSS import. Without it, map controls and popups render unstyled. Functional, but visually broken.
Step 2: Create a Map Container
The SDK fills whatever element you point it at, so you need a <div> with an explicit height. This is the single most common setup mistake: if the container has height: 0, the map initializes but stays invisible.
<div id="map" style="width: 100%; height: 500px;"></div>
A fixed pixel value or viewport unit (100vh, 50vh) both work. Percentage heights only work if the parent element also has a defined height.
Step 3: Render Your First Interactive Map
Three lines of configuration are all it takes: a container, a style URL with your API key, and a starting position.
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'll see a vector map you can drag, scroll, and pinch to navigate. Tiles load on demand from MapAtlas servers, so there's no heavy upfront download.
Choosing a Map Style
Swap the path segment in the style URL to change the look entirely:
| Style | URL Path | Best for |
|---|---|---|
| Basic | /styles/basic/style.json | General-purpose apps |
| Bright | /styles/bright/style.json | Data visualization overlays |
| Dark | /styles/dark/style.json | Dashboards, night mode, analytics |
Quick win: Use the Dark style for admin panels and tools used in low-light environments. It reduces eye strain and makes data layers like heatmaps and route lines visually pop against the background.
Step 4: Add Markers and Popups to Your Map
A map without markers is just a background image. Markers turn a static view into something users can interact with.
Single Marker with a 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 and the popup opens. You can put any HTML inside: addresses, thumbnails, CTA buttons, whatever your UI calls for.
Plotting Multiple Markers from Data
Most real-world apps need more than one pin. Loop over an array and create a marker for each entry:
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);
});
Performance note: Once you exceed roughly 100 to 200 markers, rendering slows noticeably on zoomed-out views. Enable GeoJSON source clustering (supported out of the box by the SDK) to group nearby markers at low zoom levels. Check the SDK docs for clustering configuration.
Step 5: Add Address Search with the Geocoding API
The Geocoding API turns a text query (a street address, city name, or landmark) into coordinates you can pan to, mark, or feed into a routing request.
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;
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);
}
// Try it
searchAddress('Rijksmuseum, Amsterdam');
Results come back as GeoJSON features, so they plug directly into any GeoJSON-compatible layer, data table, or downstream API call.
Build a live search bar in under 30 lines: Attach
searchAddressto theinputevent of a text field, debounce it by 300ms, and you've got autocomplete-style map search with no extra dependencies.
Integrating Interactive Maps with React
A Reusable Map Component
Wrap the map initialization in useEffect so it runs after the DOM mounts, and return a cleanup function to prevent memory leaks on unmount:
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();
}, [apiKey]);
return <div ref={containerRef} style={{ width: '100%', height: '500px' }} />;
}
Use it anywhere in your component tree:
<MapAtlasMap
apiKey={process.env.NEXT_PUBLIC_MAPATLAS_KEY}
center={[4.9041, 52.3676]}
zoom={13}
/>
Handling Next.js Server-Side Rendering
The map SDK depends on browser APIs (window, document) that don't exist during SSR. Import the component dynamically with SSR disabled:
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 }} />
),
}
);
The loading placeholder keeps your layout stable while the map bundle downloads, preventing cumulative layout shift (CLS), which matters for both user experience and Core Web Vitals.
Production Performance Checklist
Before shipping, run through these optimizations:
- Lazy-load maps below the fold. Use
IntersectionObserverto initialize the map only when its container scrolls into view. This defers ~200 KB of JavaScript from the initial page load. - Stick with vector tiles. Vector tiles scale cleanly to any screen density, load faster than raster images, and can be restyled client-side without additional server requests. MapAtlas serves vector tiles by default.
- Cluster large marker sets. Beyond 100 to 200 markers, unclustered rendering on a zoomed-out view causes noticeable frame drops. Clustering solves this entirely.
- Keep your API key server-side. Never commit keys to a public repository. Use environment variables (
NEXT_PUBLIC_MAPATLAS_KEYin Next.js) or proxy requests through your backend for sensitive operations. - Set
maxBoundsfor regional apps. If your users only care about one geography, restrict the viewport so tiles outside that area are never requested. Fewer network calls, faster load.
What to Build Next
You've got a map that renders, shows markers, searches addresses, and integrates with React. Here's where to go from here:
- Routing API: Request turn-by-turn directions between two coordinates. Returns a route polyline, total distance, and estimated travel time.
- Isochrone API: Generate a polygon covering every point reachable within n minutes. Used for delivery zones, service coverage maps, and catchment area analysis.
- Matrix API: Calculate travel time and distance between multiple origins and destinations in a single request. Essential for fleet dispatch and logistics optimization.
Full SDK reference, style documentation, and API guides are available at docs.mapatlas.xyz.
Frequently Asked Questions
Can I add interactive maps to my website for free?
Yes. MapAtlas offers a free tier with no credit card required at sign-up. It includes vector tile rendering, the Geocoding API, and the Routing API. That's enough for development and small-scale production use.
How do I embed a map in a React or Next.js app?
Wrap the map initialization in a useEffect hook so it runs after the DOM mounts. In Next.js, use dynamic() with ssr: false to avoid server-side rendering errors. Both approaches are covered with copy-paste examples in this tutorial.
What are vector tiles, and why should I use them over raster?
Vector tiles describe map features (roads, buildings, labels) as mathematical geometry rather than pre-rendered pixel images. They scale sharply to any resolution, download faster, and can be restyled entirely on the client without additional server round-trips.
How many markers can I add before performance drops?
Rendering typically degrades beyond 100 to 200 markers at low zoom levels. The fix is clustering: the MapAtlas SDK supports GeoJSON source clustering natively, grouping nearby markers at low zoom and expanding them as the user zooms in.
Do I need GIS experience to use MapAtlas?
No. The SDK is designed for web developers, not GIS specialists. You initialize a map with coordinates and a zoom level, add markers with longitude/latitude pairs, and call the Geocoding API with plain text. No spatial databases or GIS tooling required.
