Use Mapbox GL JS in a Svelte app

27 mins remaining
Use Mapbox GL JS in a Svelte app
Intermediate
codeJavaScript
Prerequisite
Familiarity with Svelte and front-end development concepts.

Svelte is a popular JavaScript web framework used for increasing client performance. Since Svelte manipulates the DOM, it can be confusing to connect Svelte with other libraries that also manipulate the DOM and manage state — like Mapbox GL JS.

In this tutorial, you will learn how to create a Svelte web app that uses Mapbox GL JS to render a map, dynamically display the coordinates of the map center point and its zoom level when a user interacts with the map and be able to reset the map view with an UI element.

This tutorial shows code for Svelte Bindings and Component Lifecycles.

Getting started

You will need a few things in place before starting this tutorial:

  • A Mapbox access token. Your Mapbox access tokens are on the Access Tokens page of your Developer Console.
  • Mapbox GL JS. Mapbox GL JS is a JavaScript library used for building web maps.
  • A text editor. Use the text editor of your choice for writing HTML, CSS, and JavaScript.
  • Node.js and npm. To run the commands necessary to run your Svelte app locally, install Node.js and npm.
  • Working familiarity with Svelte. You don't need to have a lot of experience using Svelte to complete this tutorial, but you should be familiar with the underlying concepts and workflows.

Create a Svelte app with Vite

To get started, we'll use Vite JS to quickly scaffold a Svelte project. This will create a base layer to build our Svelte app upon. Wherever you want the folder for this project to exist on your computer, navigate to that directory via the command line and run the following command:

$npm create vite@latest use-mapbox-gl-js-with-svelte -- --template svelte

Follow the instructions to change directories, install dependencies (npm install) run the development server (npm run dev).

$npm run dev

> use-mapbox-gl-js-with-svelte@0.0.0 dev
> vite

Forced re-optimization of dependencies

VITE v6.0.7 ready in 566 ms

➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help

Load the provided URL (http://localhost:5173/) in your browser and you should see the development server running.

circlecirclecircle
arrow-leftarrow-rightrotate
heartmapboxmenu

Next you need to install Mapbox GL JS in this project. Run the following command:

$npm install mapbox-gl

Open the project you've created, navigate to the src/app.css file. This contains the CSS styles to create the page we saw above. We don't need any of these styles in our app, so delete all existing CSS and add the snippet below.

src/app.css
body {
margin: 0;
padding: 0;
}

Create the map container & add the map

Following Svelte patterns, all the following code (including HTML, CSS, and JavaScript) will occur in the src/App.svelte file. Open this file.

This app will include a Mapbox map, JavaScript to handle storing and updating information about the map, a sidebar <div> to show that information, and CSS to style the <div>. You should set up the bones of your app like this; we will handle the map itself later. Delete any HTML in App.svelte and replace it with the following:

src/App.svelte
<script></script>

<!-- Your HTML will go here -->

<style></style>

Add Mapbox GL JS

Add the following import statements between your <script> tags to import the Map object from Mapbox GL and Mapbox GL's CSS to your Svelte app:

src/App.svelte
<script>
 
import { Map } from 'mapbox-gl';
 
import 'mapbox-gl/dist/mapbox-gl.css';
</script>

<style></style>

Add variables

To load a map on a webpage with Svelte, we need to create variables for the map, the HTML element that will contain the map, and the map's starting position (including longitude, latitude, and zoom level).

Add the following between your <script> tags, below your import statements:

src/App.svelte
<script>
import { Map } from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
 

 
let map;
 
let mapContainer;
 
let lng, lat, zoom;
 

 
lng = -71.224518;
 
lat = 42.213995;
 
zoom = 9;
 

 
let initialState = { lng, lat, zoom };
</script>

<style></style>

These positional values will create a map centered near Boston, Massachusetts.

Use Svelte binding

The Mapbox map needs certain attributes defined immediately to work, such as the map's center and container. This container is an <div> that the map renders in.

To do this, we will use Svelte binding. Insert this HTML between your script & style tags.

src/App.svelte
<script>
import { Map } from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';

let map;
let mapContainer;
let lng, lat, zoom;

lng = -71.224518;
lat = 42.213995;
zoom = 9;

let initialState = { lng, lat, zoom };
</script>

 
<div class="map" bind:this={mapContainer} />

<style></style>

This "binds" the variable mapContainer to the <div> with class "map". Once we instantiate the map (in the next step) and reference the mapContainer then when the page is rendered the map will be loaded into this div.

Add map CSS

Now that there exists an HTML element with a class of map, we can apply CSS styling to that class. Between your empty <style> tags, add the following:

 
<style>
 
.map {
 
position: absolute;
 
width: 100%;
 
height: 100%;
 
}

Add Svelte lifecycle

You will import Svelte functions right below where you imported the Map object from Mapbox GL. On the next line, add:

src/App.svelte
<script>
import { Map } from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
 
import { onMount, onDestroy } from 'svelte';

let map;
let mapContainer;
let lng, lat, zoom;
...

onMount and onDestroy are Svelte lifecycle functions. onMount in particular runs after a component is first rendered to the DOM. The creation of the map should go in onMount.

Add the following directly underneath your variable declarations and definitions. Note: YOUR_MAPBOX_ACCESS_TOKEN is required here. Log in to console.mapbox.com if it is not already present in the snippet below.

src/App.svelte
 
onMount(() => {
 
map = new Map({
 
container: mapContainer,
 
accessToken: "YOUR_MAPBOX_ACCESS_TOKEN",
 
center: [initialState.lng, initialState.lat],
 
zoom: initialState.zoom
 
});
 
});
 

 
onDestroy(() => {
 
map.remove();
 
});

At this point, you've added enough code to load a Mapbox map in a Svelte app. Run npm run dev in your app's root directory to launch the development server, and you should see an interactive map of Boston.

Respond to map interactions

This app will respond to events generated by user interactions and will be able to store and render the longitude, latitude, and zoom for the map in a UI element. These values will all change as your user interacts with the map, and the UI will update with it.

Add UI HTML

As we've already created these variables (lng, lat, and zoom), it's time to add to the <div> that will become the informational sidebar UI element. Add the following HTML into your app, below the <div> with class "map".

src/App.svelte
...
</script>

<div class="map" bind:this={mapContainer} />

 
<div class="sidebar">
 
Longitude: {lng.toFixed(4)} | Latitude: {lat.toFixed(4)} | Zoom:
 
{zoom.toFixed(2)}
 
</div>

<style>
...

Add sidebar CSS

If you run npm run dev at this point, you will see that the map's initial position and zoom are displayed above the map; The presentation is not styled and does not change when interacting with the map.

We should style the display. Between your <style> tags, under the rules for the map class, add the following:

src/App.svelte
...

<style>
.map {
position: absolute;
width: 100%;
height: 100%;
}
 
.sidebar {
 
background-color: rgb(35 55 75 / 90%);
 
color: #fff;
 
padding: 6px 12px;
 
font-family: monospace;
 
z-index: 1;
 
position: absolute;
 
top: 0;
 
left: 0;
 
margin: 12px;
 
border-radius: 4px;
 
}
</style>

Note: at this moment the numbers in our Sidebar UI are static as we haven't yet responded to user interactions.

Update center and zoom information

Next, you need to create a function that stores the new latitude, longitude, and zoom that you get when a user interacts with the map. We'll use a map.on() function with a 'move' event inside the onMount() to reset the value of these variables when a user moves the map. Add the updateData() function and map.on function as seen below.

src/App.svelte
 
function updateData() {
 
zoom = map.getZoom();
 
lng = map.getCenter().lng;
 
lat = map.getCenter().lat;
 
}
 
map.on('move', () => {
 
updateData();
 
});

Control the map with external events

As a last step to show manipulating the map from our Svelte app, we will add a reset button that resets the map view to the initial view state.

Add HTML & CSS

Add this <button> HTML below your sidebar <div> and the following CSS into your <style> tags.

src/App.svelte
 
<button onclick={handleReset} class="reset-button">Reset</button>
 
.reset-button {
 
position: absolute;
 
top: 50px;
 
z-index: 1;
 
left: 12px;
 
padding: 4px 10px;
 
border-radius: 10px;
 
cursor: pointer;
 
}

Add click handler

Lastly we'll add our handleReset function into our <script> tag below our onDestroy() callback.

src/App.svelte
...
onDestroy(() => {
map.remove();
});

 
function handleReset() {
 
map.flyTo({
 
center: [initialState.lng, initialState.lat],
 
zoom: initialState.zoom,
 
essential: true
 
});
 
}
</script>
...

This function uses the flyTo method to animate the camera object to the initial view, using the initial center coordinates & zoom.

PLAYGROUND
Location Helper

To experiment with camera pitch, bearing, tilt, and zoom and get values to use in your code, try our Location Helper tool.

Final product

The final App.svelte page will look like the following:

src/App.svelte
<script>
import { Map } from 'mapbox-gl';
import { onMount, onDestroy } from 'svelte';
import 'mapbox-gl/dist/mapbox-gl.css';

let map;
let mapContainer;
let lng, lat, zoom;

lng = -71.224518;
lat = 42.213995;
zoom = 9;

let initialState = { lng, lat, zoom };

function updateData() {
zoom = map.getZoom();
lng = map.getCenter().lng;
lat = map.getCenter().lat;
}

onMount(() => {

map = new Map({
container: mapContainer,
accessToken: "YOUR_MAPBOX_ACCESS_TOKEN",
center: [initialState.lng, initialState.lat],
zoom: initialState.zoom
});

map.on('move', () => {
updateData();
});
});

onDestroy(() => {
map.remove();
});

function handleReset() {
map.flyTo({
center: [initialState.lng, initialState.lat],
zoom: initialState.zoom,
essential: true
});
}
</script>

<div class="map" bind:this={mapContainer}></div>
<div class="sidebar">
Longitude: {lng.toFixed(4)} | Latitude: {lat.toFixed(4)} | Zoom:
{zoom.toFixed(2)}
</div>
<button onclick={handleReset} class="reset-button">Reset</button>


<style>
.map {
position: absolute;
width: 100%;
height: 100%;
}

.sidebar {
background-color: rgb(35 55 75 / 90%);
color: #fff;
padding: 6px 12px;
font-family: monospace;
z-index: 1;
position: absolute;
top: 0;
left: 0;
margin: 12px;
border-radius: 4px;
}

.reset-button {
position: absolute;
top: 50px;
z-index: 1;
left: 12px;
padding: 4px 10px;
border-radius: 10px;
cursor: pointer;
}
</style>

Next steps

Congratulations! You have created a Svelte app that uses Mapbox GL JS to render a map, display the center coordinates and the zoom level of the map, and then update that display when a user interacts with the map.

What we covered

  • Creating a Svelte App with Vite JS
  • Importing Mapbox GL JS into your app
  • Rendering a Map
  • Displaying a Sidebar with Map data that updates dynamically
  • Resetting a map's view

Now that you have created a Svelte app that uses Mapbox GL JS, explore more functionality of GL JS in examples, or learn about Mapbox Studio and how you can create custom map styles and upload your own map datasets.

Was this tutorial helpful?