How to display a map in Vue.js using MapLibre GL JS

   

In this tutorial, you’ll learn how to create a Vue.js component to render a map using MapLibre GL JS. Together we will make a simple fullscreen map application as an example of how to use MapTiler maps together with Vue.js and MapLibre GL JS for your Vue.js app.

By the end of this tutorial, you will be able to create a full-screen map with a marker at a specified location. Your final map will look like this:

Getting started

Minimal requirements for completing this tutorial.

  • Some experience with Vue.js. You don’t need a lot of experience using Vue.js for this tutorial, but you should be familiar with basic concepts and workflow.
  • MapTiler API key. Your MapTiler account access key is on your MapTiler Cloud account page or Get API key for FREE.
  • MapLibre GL JS. Javascript library for building web maps. In this tutorial, we will see how to install it.
  • Node.js and npm. Necessary to run your Vue.js app locally. https://nodejs.org
  • Vue CLI. You need to have the Vue CLI installed. To install the Vue CLI, open a terminal window and run the following command:
npm install -g @vue/cli

Create an app

In this step, we will learn how to create a Vue.js app.

To create a new Vue.js project run in your command-line:

vue create my-vue-map

The vue create command prompts you for information about features to include in the initial app. Select the Default (Vue 3) ([Vue 3] babel, eslint) option.

Use the arrow keys and press the Enter or Return key to select an option. The Vue CLI installs the necessary Vue.js npm packages and other dependencies and creates a new workspace and a simple Welcome app, ready to run. For more information, follow Creating a Project.

Navigate to the newly created project folder my-vue-map

cd my-vue-map

Inside the newly created project, you can run npm run serve to start your local environment. You will find your app on address http://localhost:8080/.

Now you should see the app in your browser.

 

Installation and setting up

Install MapLibre GL library. Navigate to your project folder and run the command:

npm i maplibre-gl

Now navigate to the src folder and delete all the content of the App.vue file

Write the following lines in the App.vue file

<template>
  <div class="app">
    This is my map App
  </div>
</template>

<script>

export default {
  name: 'App',
  components: {
  }
}
</script>

<style>
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

.app {
  text-align: center;
}
</style>

Now you should see “This is my map App“ in your browser.

Navigate to the src/components folder and delete de HelloWorld.vue file

Create a navbar component

We will create a simple heading navbar component.

Create a new file called Navbar.vue inside the components folder and write these lines:

<template>
  <div class="heading">
    <h1>This is my map App</h1>
  </div>
</template>

<script>
export default {
  name: 'Navbar'
}
</script>

<style scoped>
.heading {
  margin: 0;
  padding: 0px;
  background-color: black;
  color: white;
}

.heading > h1 {
  padding: 20px;
  margin: 0;
}
</style>

Finally, to display the Navbar, we need to import the Navbar component and add it to our main component template section App.vue.

Import the navbar component into App.vue script block

<script>
import Navbar from './components/Navbar.vue';

export default {
  name: 'App',
  components: {
    Navbar
  }
}
</script>

Replace the text ‘This is my map App’ with <Navbar />. Your App.vue file should look like this:

<template>
  <div class="app">
    <Navbar />
  </div>
</template>

<script>
import Navbar from './components/Navbar.vue';

export default {
  name: 'App',
  components: {
    Navbar
  }
}
</script>

<style>
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

.app {
  text-align: center;
}
</style>

Now you should see the black navbar at the top of your browser.

Create a map component

Now we will create a simple map component.

Create a new file called Map.vue inside the components folder and write these lines:

<template>
  <div class="map-wrap">
    <a href="https://www.maptiler.com" class="watermark"><img
        src="https://api.maptiler.com/resources/logo.svg" alt="MapTiler logo"/></a>
    <div class="map" ref="mapContainer"></div>
  </div>
</template>

<script>
import { Map } from 'maplibre-gl';
import { shallowRef, onMounted, onUnmounted, markRaw } from 'vue';

export default {
  name: "Map",
  setup () {
    const mapContainer = shallowRef(null);
    const map = shallowRef(null);

    onMounted(() => {
      const apiKey = 'YOUR_MAPTILER_API_KEY';

      const initialState = { lng: 139.753, lat: 35.6844, zoom: 14 };

      map.value = markRaw(new Map({
        container: mapContainer.value,
        style: `https://api.maptiler.com/maps/streets/style.json?key=${apiKey}`,
        center: [initialState.lng, initialState.lat],
        zoom: initialState.zoom
      }));

    }),
    onUnmounted(() => {
      map.value?.remove();
    })

    return {
      map, mapContainer
    };
  }
};
</script>


<style scoped>
@import '~maplibre-gl/dist/maplibre-gl.css';

.map-wrap {
  position: relative;
  width: 100%;
  height: calc(100vh - 77px); /* calculate height of the screen minus the heading */
}

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

.watermark {
  position: absolute;
  left: 10px;
  bottom: 10px;
  z-index: 999;
}
</style>

We use position: absolute on the map itself and position: relative on the wrap around the map for more possibilities in future styling.

Here you will need to replace YOUR_MAPTILER_API_KEY with your MapTiler API key.

  1. The container option sets the DOM element where you want to place your map. We will assign the mapContainer ref expected by our component to an HTML element, which will act as a container. Keep in mind that the reference to mapContainer can only be used after the execution of the onMounted hook.
  2. The style option defines what style the map is going to use.
  3. The center and zoom options set the starting position of the map.

The onUnmounted function does the cleanup when the component instance is destroyed.

Finally, to display the Map, we need to import the Map component and add it to our main component template section App.vue.

Import the map component into App.vue script block

<script>
import Navbar from './components/Navbar.vue';
import Map from './components/Map.vue';

export default {
  name: 'App',
  components: {
    Navbar,
    Map
  }
}
</script>

Add the <Map /> just below the Navbar in the template section. The template block should look like this

<template>
  <div class="app">
    <Navbar />
    <Map />
  </div>
</template>

With everything done up until now, you should be able to see your beautiful map in your browser.

Your App.vue file should look like this:

<template>
  <div class="app">
    <Navbar />
    <Map />
  </div>
</template>

<script>
import Navbar from './components/Navbar.vue';
import Map from './components/Map.vue';

export default {
  name: 'App',
  components: {
    Navbar,
    Map
  }
}
</script>

<style>
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

.app {
  text-align: center;
}
</style>

Basic additional options

The last topic of this tutorial will be adding basic objects to your map. For more detailed information, you can visit the MapLibre documentation.

Map Controls

We will navigate back to our Map.vue file and add map navigation controls to our map.

Add the NavigationControl next to the Map object import from MapLibre GL.

import { Map, NavigationControl } from 'maplibre-gl';

On line 30 (just after the initialization of the map) of the Map.vue file, add the following line:

map.value.addControl(new NavigationControl(), 'top-right');

new NavigationControl() will create new controls object which we add to current map using the addControl() function in the 'top-right' position.

Another essential thing to add to your map could be a marker of some location.

Add the Marker next to the Map object import from MapLibre GL.

import { Map, NavigationControl, Marker } from 'maplibre-gl';

In the following line where we declare the navigation control, we add these lines:

new Marker({color: "#FF0000"})
  .setLngLat([139.7525,35.6846])
  .addTo(map.value);

We create a new marker using the .marker function. We added the color option to make it red, then set Lng/Lat of the marker using .setLngLat() function , and finally added it to the current map using .addTo() function.

We finished our basic map objects and your Map.vue file should look like this:

<template>
  <div class="map-wrap">
    <a href="https://www.maptiler.com" class="watermark"><img
        src="https://api.maptiler.com/resources/logo.svg" alt="MapTiler logo"/></a>
    <div class="map" ref="mapContainer"></div>
  </div>
</template>

<script>
import { Map, NavigationControl, Marker } from 'maplibre-gl';
import { shallowRef, onMounted, onUnmounted, markRaw } from 'vue';

export default {
  name: "Map",
  setup () {
    const mapContainer = shallowRef(null);
    const map = shallowRef(null);

    onMounted(() => {
      const apiKey = 'YOUR_MAPTILER_API_KEY';

      const initialState = { lng: 139.753, lat: 35.6844, zoom: 14 };

      map.value = markRaw(new Map({
        container: mapContainer.value,
        style: `https://api.maptiler.com/maps/streets/style.json?key=${apiKey}`,
        center: [initialState.lng, initialState.lat],
        zoom: initialState.zoom
      }));
      map.value.addControl(new NavigationControl(), 'top-right');
      new Marker({color: "#FF0000"})
        .setLngLat([139.7525,35.6846])
        .addTo(map.value);
    }),
    onUnmounted(() => {
      map.value?.remove();
    })

    return {
      map, mapContainer
    };
  }
};
</script>


<style scoped>
@import '~maplibre-gl/dist/maplibre-gl.css';

.map-wrap {
  position: relative;
  width: 100%;
  height: calc(100vh - 77px); /* calculate height of the screen minus the heading */
}

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

.watermark {
  position: absolute;
  left: 10px;
  bottom: 10px;
  z-index: 999;
}
</style>

Full code to download

We have created a template with the result of this tutorial that will serve as a basis to build future applications. You can access the template repository at https://github.com/maptiler/vue-template-maplibre-gl-js.

Online demo:

You can see an online demo at https://labs.maptiler.com/vue-template-maplibre-gl-js/

Conclusion

Congratulations! You have finished your simple fullscreen map app using Vue.js, showing Tokyo with a marker on Tokyo Imperial Palace. You can explore more about MapLibre GL JS for your map in the MapLibre API reference.

MapLibre GL JS

VUE JS - JavaScript Framework

NPMJS - MapLibre

MapLibre - Official Site

MapTiler Cloud - Customize map