Improve perceived performance of Mapbox GL JS using the Static Images API
Familiarity with front-end development concepts including JavaScript and API calls.
Many things can impact the performance of a Mapbox GL JS map, such as network hardware constraints, complex application requirements requiring large JavaScript payloads, or highly dense and detailed Mapbox styles. Using the Mapbox Static Images API with your Mapbox GL JS maps won't solve these performance issues, but it will allow you to improve the perceived performance for your users since you will be able to immediately show a static image while Mapbox GL JS works in the background.
In this tutorial, you will create an interface At the end of this tutorial, you will have a functioning landing page that loads a static image of your map and waits for the Mapbox GL JS map to load in the background, then swaps to the vector map once rendering is complete. This technique gives the user instant visual feedback and provides a visual buffer to allow the dynamic content to load.
Getting started
There are a few resources you’ll need before getting started:
- An access token from your Mapbox account. You will use an access token to associate a map with your account. Your access token is on the Access Token page of your Developer Console.
- A Mapbox style ID from your Mapbox account. You will use this style in both the Static Images API request and the vector map.
- A text editor. Use the text editor of your choice for writing HTML, CSS, and JavaScript.
Structure the webpage
Start by pasting the following code into your text editor. This code will create a scaffold for adding your map and website content.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Display a map</title>
<script src="https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js"></script>
<link
href="https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css"
rel="stylesheet"
/>
<style>
#content {
width: 600px;
}
</style>
</head>
<body>
<header>
<h1>Title of my webpage</h1>
</header>
<div id="content"></div>
</body>
</html>
Save this file and open it in your browser. You will only see a header at the top.
Build the map container
The next step is to specify where the map will go. Copy the following into the body tag of your page, inside the content
div.
<div id="container">
<div id="map"></div>
</div>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
This code creates three items. First, it creates a parent container for the map and the static image (container
). Second, it creates a div
for the Mapbox GL JS map (map
). Third, it creates a list in which to host some basic content.
Save your work and reload the browser page.
You should only see the header and the list content.
Add the Mapbox GL JS map
The next step is to create a Mapbox GL JS map. This map is going to be attached to the map
div you created in the last step. Its size will be 600 pixels by 400 pixels.
Underneath the closing <div>
tag in your HTML body, add a <script>
node with the following.
<script>
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN ';
const map = new mapboxgl.Map({
container: 'map', // container ID
style: 'mapbox://styles/mapbox/streets-v12', // stylesheet location
center: [-85.757, 38.25], // starting position [lng, lat]
zoom: 10 // starting zoom
});
</script>
Add CSS
The map is technically loading now, but it isn't shown on the page. Adding some styling using CSS will tell the page how big to draw the map.
Copy the CSS below and paste it into the <style>
tag of the page.
#container {
width: 600px;
height: 400px;
margin: 20px 0;
display: grid;
}
#map {
grid-column: 1;
grid-row: 1;
width: 100%;
height: 100%;
}
Test the map's performance
Now that the map is loading, you may notice that it takes a second to fetch all the data and render the map.
This may feel like a performant application. But performance can vary depending on hardware, network conditions, and map complexity, which means that the map may not load quickly under all circumstances.
Add a static image of your map
Integrating the Static Images API requires two individual steps. First, create a <div>
where the image will live and update the CSS. Second, wait for the map to load and then switch the static image off, revealing the Mapbox GL JS powered map below.
Update the HTML and CSS
In the <container>
div, add a single <div>
to house the new static image.
<div id="container">
<div id="map"></div>
<div id="static"></div>
</div>
Save and reload the page. You may notice that the original map has now been compressed. Resolve this by updating the CSS in the <style>
tag with the following rule.
#static {
width: 100%;
height: 100%;
grid-column: 1;
grid-row: 1;
}
The map will return to its previous state.
Align image to map
Now that the building blocks of the app are all in place, the final step is to fetch the static image, overlay it on the <map>
div, and wait for the Mapbox GL JS map to stop loading.
The URL format is described in the Static Images API documentation, with the following convention:
https://api.mapbox.com/styles/v1/{username}/{style_id}/static/{lng},{lat},{zoom}/{width}x{height}?{access_token}
The first point of alignment is coordinates. It is important that the URL request and the Mapbox GL JS map share the same size, centerpoint, and zoom. In this example the map is instantiated at:
center: [-85.757, 38.25], // starting position [lng, lat]
zoom: 10, // starting zoom
This translates to https://api.mapbox.com/styles/v1/mapbox/streets-v12/static/-85.757,38.25,10/
in the URL above.
The second point of alignment is sizing. The dimensions of the image should be the same as the container
div, which is 600px by 400px. This creates the final URL:
https://api.mapbox.com/styles/v1/mapbox/streets-v12/static/-85.757,38.25,10/600x400?access_token=YOUR_MAPBOX_ACCESS_TOKEN
Fetch the image
To fetch the image, change the CSS rule for #static
to include the Static Images API URL in a background-image
property.
#static {
background-image: url('https://api.mapbox.com/styles/v1/mapbox/streets-v12/static/-85.757,38.25,10/600x400?access_token=YOUR_MAPBOX_ACCESS_TOKEN');
width: 100%;
height: 100%;
grid-column: 1;
grid-row: 1;
}
If you look closely, you will see the Mapbox GL JS map and the image load, except the map will be sitting on top of the image. This is the opposite of what is needed. To fix this, edit the CSS of the #map
.
#map {
grid-column: 1;
grid-row: 1;
width: 100%;
height: 100%;
visibility: hidden;
}
This will turn off the map div
, leaving the static image in its place. You will notice the page loads instantly, but now the image stays put and no vector map loads.
Swap when the map loads
Setting visibility: hidden
does not prevent the map from loading. It only prevents users from seeing the map load. You can use this behavior and Mapbox GL JS event listeners to toggle the static image off when the map has loaded completely.
The event to listen for is map.on('load)
. To take advantage of this event, add the following to your <script>
tag.
map.on('load', () => {
const mapContainerEl = document.getElementById('map');
mapContainerEl.style.visibility = 'visible';
});
This creates an event listener that waits until Mapbox GL JS fires the load
event, which only happens once, then selects the map div
and toggles the visibility
setting.
Final product
You have now completed the example from the beginning of the tutorial.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>
Demo: Improve perceived performance of Mapbox GL JS using the Static
Images API
</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js"></script>
<link
href="https://api.tiles.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css"
rel="stylesheet"
/>
<style>
#content {
width: 600px;
}
#container {
width: 600px;
height: 400px;
margin: 20px 0;
display: grid;
}
#map {
grid-column: 1;
grid-row: 1;
width: 100%;
height: 100%;
visibility: hidden;
}
#static {
background-image: url('https://api.mapbox.com/styles/v1/mapbox/streets-v11/static/-85.757,38.25,10/600x400?access_token={{MAPBOX_ACCESS_TOKEN}}');
width: 100%;
height: 100%;
grid-column: 1;
grid-row: 1;
}
</style>
</head>
<body>
<header>
<h1>Title of my webpage</h1>
</header>
<div id="content">
<div id="container">
<div id="map"></div>
<div id="static"></div>
</div>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<script>
mapboxgl.accessToken = '{{MAPBOX_ACCESS_TOKEN}}';
const map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/streets-v12', // stylesheet location
center: [-85.757, 38.25], // starting position [lng, lat]
zoom: 10 // starting zoom
});
map.on('load', () => {
const mapContainerEl = document.getElementById('map');
mapContainerEl.style.visibility = 'visible';
});
</script>
</body>
</html>
For more information about Mapbox Static Images API, read the product documentation. For more information about Mapbox GL JS, read the product documentation and try out the examples.