With the rise of AI coding tools, our approach to helping developers get started with Mapbox GL JS has had to adapt. These days you're probably building with Claude Code, Codex, or Gemini — and when it comes time to add a map to your project, being specific about the tools and technology you name in your prompt makes a bigger difference than you might expect.
In this post, I'll start with a sample dataset and show you the real-world output of a few different prompts to Claude Code. The first prompt will be generic. The second will call for Mapbox products by name. The third will push further into custom UI. You'll see the difference immediately, and understand why all web maps are not created equal.
But first, GeoJSON data!
For a sample dataset, I created a GeoJSON FeatureCollection of three point features: landmark buildings in Manhattan, New York City. I've included properties like name, description, and imageUrl — the kind of thing we'd want to display to an interested map user. You can use geojson.io to craft your own GeoJSON data when building spatial apps.
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-73.9857, 40.7484]
},
"properties": {
"name": "Empire State Building",
"description": "An iconic 102-story Art Deco skyscraper completed in 1931. Once the tallest building in the world, it remains one of the most recognizable landmarks in New York City.",
"yearBuilt": 1931,
"imageUrl": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Empire_State_Building_%28aerial_view%29.jpg/500px-Empire_State_Building_%28aerial_view%29.jpg"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-73.9755, 40.7516]
},
"properties": {
"name": "Chrysler Building",
"description": "A stunning Art Deco masterpiece completed in 1930, famous for its terraced crown of radiating sunburst arches and gleaming stainless steel cladding.",
"yearBuilt": 1930,
"imageUrl": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/76/Chrysler_Building_by_David_Shankbone_Retouched.jpg/500px-Chrysler_Building_by_David_Shankbone_Retouched.jpg"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-73.9772, 40.7527]
},
"properties": {
"name": "Grand Central Terminal",
"description": "A world-famous Beaux-Arts railroad terminal opened in 1913. Its Main Concourse features a celestial ceiling mural and massive arched windows.",
"yearBuilt": 1913,
"imageUrl": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/71/Image-Grand_central_Station_Outside_Night_2.jpg/500px-Image-Grand_central_Station_Outside_Night_2.jpg"
}
}
]
}
The generic web map prompt
For demonstration purposes, I'm using simple, single-file web pages with JS in a <script> tag.
If you write a prompt like this:
In index.html, make an interactive map with
markers for each Feature in `landmarks.geojson`
You might get a map like this:
Without being told what to use, the AI coding tool reached for Leaflet and a free raster tileset published by OpenStreetMap. It did what you asked, and even went a step further by adding popups that appear on click. But the resulting map feels clunky, dated, and not like something you'd actually want to ship.
Leaflet is a tried-and-true open source mapping library, but it comes from an era — over a decade ago — before vector-based maps came on the scene. It works by arranging PNG image tiles in a div, with more PNGs or SVGs layered on top for markers and overlays. The basemap you see here is actually called "OpenStreetMap's Standard Tile Layer," and it's intended as a reference basemap for people making edits to OpenStreetMap itself. Used as a basemap in front of real users, it's busy and distracting. It's not a bad tileset — it's just designed for a very different purpose.
Mapbox GL JS: ask for it by name!
Now let's request the exact same map, but specify Mapbox products explicitly:
In index.html, add an interactive map with
markers for each feature in landmarks.geojson
using the latest version of Mapbox GL JS and
the Mapbox Standard Style.
Once the code is generated, your AI tool will remind you to add your access token. Mapbox GL JS includes a generous free tier (50,000 map loads per month), but all usage needs to be tied to an account, so one small edit is required before the map will actually load:
Created index.html using Mapbox GL JS v3.27.0 with
the Standard style. You'll need to replace
YOUR_MAPBOX_ACCESS_TOKEN on line 30 with your
Mapbox access token for it to work.
You'll find the placeholder right before the call that instantiates the map. Swap in a token from your Mapbox account page and you're ready to go:
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/standard',
center: [-73.9800, 40.7505],
zoom: 14
});
You'll immediately see — and feel — the difference. Scroll a bit and you'll experience smooth, incremental zooming. Basemap features fade in and out gradually, surfacing the most important information for whatever part of the planet you're looking at. Hold control and drag up and down, or left and right, and you can pitch and rotate the map, controlling the camera like you would in a video game.
The two key things you asked for were Mapbox GL JS, our best-in-class JavaScript mapping library, and the Mapbox Standard Style, our versatile, configurable 2D/3D basemap. The Standard Style is continuously updated with better data for the whole world, and our professional cartographers are constantly refining its design and usability.
The basemap is worlds apart from where we started, but the markers and popups themselves are still fairly close to defaults. So let's go a step further with a prompt aimed squarely at the interaction design.
Adding UI/UX polish
A classic marker-and-popup map may be all you need, but it can still feel generic. One more prompt gets you custom basemap configuration, camera moves, non-map UI elements, and dynamic markers, all working together:
Hide basemap Point of interest labels.
Use a circle layer instead of markers.
Add a symbol layer using label-bg.png as a
stretchable image to display a label above
each circle.
When clicked, animate the circle to a larger
radius, gray out or subdue the other features,
zoom and pitch the map to show the selected
feature, and animate an info panel in from the
bottom (image on the left, info on the right).
When the panel is closed, restore the original
camera view.
There's a lot packed into that prompt, but take a look at the result first, and then I'll walk through each piece:
First we remove point of interest labels, one of many configurable properties of the Mapbox Standard Style. You can also modify individual colors of major features like water, land, or streets, hide 3d models, and apply new color themes. Explore all of the options in the Mapbox Standard Style Playground.
Instead of generic Mapbox GL JS markers, each point is now represented by a circle. More importantly, those circles live in a circle layer — as opposed to markers, which are HTML elements positioned above the map — which makes them far more performant, since they're rendered directly by the WebGL engine that powers the map. This approach scales up to many thousands of features, and works with both GeoJSON and vector tile data sources.
We also added a label to each feature, which is a nice touch that lets users know what the circle represents without exposing all of its underlying data up front. The label uses a symbol layer with a stretchable image as its background. Like the circles, it's WebGL-rendered and will scale with whatever data you bring in.
Here's label-bg.png, the source image used for the label background. Any image can be stretched this way — the stretchable regions are defined in code, so the label background expands cleanly no matter how long the text inside it is:
map.addImage('label-bg', labelImg, {
content: [8, 6, 28, 18],
stretchX: [[8, 28]],
stretchY: [[6, 18]]
});
The fun part happens when a circle is clicked:
- Save the original camera view (center, zoom, pitch, bearing).
- Update the paint properties of the circle and label layers, enlarging the active feature and subduing the others.
- Populate the UI panel with data from the clicked feature.
- "Ease" the map into a nicely pitched view focused on the selected feature, showing 3D buildings with a smooth animation.
If you want to go deeper, here's the code that fires on click — all of it happening inside one concise openPanel() function:
function openPanel(props, coords) {
// Save original view on first activation
if (!activeFeatureName) {
originalView = {
center: map.getCenter(),
zoom: map.getZoom(),
pitch: map.getPitch(),
bearing: map.getBearing()
};
}
activeFeatureName = props.name;
// Enlarge active circle, subdue inactive features
map.setPaintProperty('landmarks-circles', 'circle-radius', [
'case',
['==', ['get', 'name'], activeFeatureName], ACTIVE_RADIUS,
DEFAULT_RADIUS
]);
map.setPaintProperty('landmarks-circles', 'circle-color', [
'case',
['==', ['get', 'name'], activeFeatureName], '#3b82f6',
'#b0b8c4'
]);
map.setPaintProperty('landmarks-circles', 'circle-stroke-color', [
'case',
['==', ['get', 'name'], activeFeatureName], '#ffffff',
'#dde1e7'
]);
map.setPaintProperty('landmarks-circles', 'circle-opacity', [
'case',
['==', ['get', 'name'], activeFeatureName], 1,
0.5
]);
map.setPaintProperty('landmarks-circles', 'circle-stroke-opacity', [
'case',
['==', ['get', 'name'], activeFeatureName], 1,
0.4
]);
map.setPaintProperty('landmarks-labels', 'text-opacity', [
'case',
['==', ['get', 'name'], activeFeatureName], 1,
0.3
]);
map.setPaintProperty('landmarks-labels', 'icon-opacity', [
'case',
['==', ['get', 'name'], activeFeatureName], 1,
0.3
]);
// Update panel content
panelImage.src = props.imageUrl || '';
panelImage.style.display = props.imageUrl ? 'block' : 'none';
panelName.textContent = props.name;
panelMeta.textContent = props.yearBuilt ? `Built ${props.yearBuilt}` : '';
panelDesc.textContent = props.description;
panel.classList.add('open');
// Zoom and pitch to feature
map.easeTo({ center: coords, zoom: 16, pitch: 50, duration: 800 });
}
With this prompt, we've gone from a fairly simple marker map to a genuinely compelling custom interaction. The underlying data hasn't changed at all — only the experience built on top of it.
Why naming your tools matters
AI coding assistants are only as specific as the prompts they're given. When you leave the choice of mapping library and basemap up to the model, it defaults to whatever shows up most often in its training data — which, for web maps, tends to be older, simpler, and less visually polished tooling. That's a reasonable default for a quick prototype, but it's rarely what you want for a production app.
The fix is straightforward: treat your AI coding tool the way you'd treat a new contractor on your team. Tell it exactly which library, which version, and which style or theme you want, the same way you would in a technical spec or a code review comment. A few extra words in your prompt — "using the latest version of Mapbox GL JS and the Mapbox Standard Style" — is the difference between a map that feels like 2012 and one that feels like it belongs in your product today.
Try it yourself
If you want to experiment with this on your own data:
- Build a small GeoJSON file with geojson.io.
- Ask your AI coding tool for a map with no library specified, and see what you get.
- Re-run the prompt, this time naming Mapbox GL JS and the Mapbox Standard Style explicitly.
- Push further with a UI/UX prompt of your own — try custom layers, click interactions, or animated camera moves.
- Compare the results — check out the Mapbox GL JS examples page for more patterns like clustering, custom markers, and popups you can ask your AI tool to replicate. Drop a link straight into your prompt, or, better yet, connect the Mapbox docs MCP Server so your coding tool can look up Mapbox APIs on its own.
Once you see the difference side by side, "ask for it by name" becomes second nature — and it's a habit worth carrying into every prompt you write for a mapping feature, not just your first one.

Top comments (0)