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.
You will need a few things in place before starting this tutorial:
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.
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.
body {
margin: 0;
padding: 0;
}
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:
<script></script>
<!-- Your HTML will go here -->
<style></style>
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:
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:
These positional values will create a map centered near Boston, Massachusetts.
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.
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
.
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:
You will import Svelte functions right below where you imported the Map
object from Mapbox GL. On the next line, add:
<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.
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.
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.
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".
...
</script>
<div class="map" bind:this={mapContainer} />
<div class="sidebar">
Longitude: {lng.toFixed(4)} | Latitude: {lat.toFixed(4)} | Zoom:
{zoom.toFixed(2)}
</div>
<style>
...
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:
...
<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.
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.
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 this <button>
HTML below your sidebar <div>
and the following CSS into your <style>
tags.
Lastly we'll add our handleReset
function into our <script>
tag below our onDestroy()
callback.
...
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.
To experiment with camera pitch, bearing, tilt, and zoom and get values to use in your code, try our Location Helper tool.
The final App.svelte
page will look like the following:
<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>
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.
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.