Skip to content
Switch branches/tags
This branch is even with CartoDB:master.
Fetch upstream

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

Vue.js introductory workshop


This workshop aims to give you a first approach to Vue.js JavaScript framework. The intention is to provide a quick overview of this framework capabilities, to be delivered in less than two hours, and for a broad audience.


At CARTO and more specifically at the Solutions team we are requested to deliver small to medium applications. Those applications, in general, need to be developed quickly and using last CARTO technology and are aimed to demonstrate CARTO platform capabilities. Using a web framework makes all the sense to speed up the development, use common practices and in general be more effective.

In the past, following CARTO product practice, we used Backbone.js to have models, collections, and views but we never went too far on its usage, as we moved out from web development to work more on BUILDER demos. Lastly, with the revamped efforts on CARTO ENGINE, we are getting more requests to develop small demos again, so we looked again to the 2018/2019 JavaScript panorama for more effective alternatives.

At the same time, CARTO Engineering team started new development projects and opted for Vue.js as their new choice, so it looked natural for us to also go in the same direction. That does not mean that we discard any other framework, but Vue.js unique adoption curve helps for our specific scenario.

From the Vue.js Guide:

Vue (pronounced /vjuː/, like **view**) is a **progressive framework** for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is easy to pick up and integrate with other libraries or existing projects.


The audience expected on this workshop needs to have some basic knowledge of HTML and JavaScript, we won't go in detail on the Airship and CARTO VL aspects, so you are expected to have also some previous experience with them to fully understand what's going on.

You only need a modern browser, but we recommend using an HTML/JS/CSS editor as well. It makes your learning easier if you leverage the official Devtools package that works with Google Chrome, Mozilla Firefox or as a standalone application.

Set up

If you clone this GitHub repository, you get a node/yarn configuration to help you set up a live reload web server. The aforementioned is totally optional, and you may want to use your server command, your code editor own server functionality, or open your files in your web browser.

To set up the configuration, you can run something like this:

yarn install
yarn serve

These commands set up and start web server pointing directly to the src folder with auto-reloading functionality.


On the src folder, you can find the different steps presented below. There is a common folder with some assets that are reused.


  • Minimal Airship + CARTO VL dashboard
  • Just with the map and some template content

Just check the HTML markup for the Airship dashboard and then how we wait for the as-responsive-content tag to be ready to load the Mapbox GL map and the VL layer.

The CARTO VL layer is placed in a .src/common/viz.js file that will be used by all the sections. This viz loads a simple point dataset along with some variables that will be exposed incrementally.


  • Add Vue.js library
  • Load a Vue app with static data
  • Bind link href property to a JavaScript expression

On this step we add a new JS file to create the Vue app with just a single static data (step). This data is rendered on the HTML file, look for the {{ step }} template.

Additionally, aside the dashboard title we add a couple of links on the same Airship item group where the href property is computed dynamically based in the step value.


  • Add a map center and zoom level to the Vue app
  • Load the map using that information
  • Every time the map is moved update it
  • Present that information in the dashboard

We add a few new properties to the Vue app data object, then a new listener to the map moveend event that will update them, and finally we expose that dynamic information on the Airship sidebar. Look for the new template calls and how they automatically update when you pan and zoom your map. No need to refer to the HTML markup from your JS code!


  • Add the CARTO VL totalCount to the Vue app
  • Add a widget with that count
  • Conditionally render a loading text until information is ready

Let's start checking our VL layer updated event to reflect the new value of totalCount into our Vue instance. This variable then rendered in our HTML but with a new feature, we use the v-if template instruction to decide if we render the count or a loading... label while the variable is not ready. Load your map and check the HTML elements in your browser developer tools to find how Vue updates dynamically the DOM to reflect the application state.


  • Add a method to format figures
  • Add a computed property as the division of two VL variables

On this section, we add a new VL variable to the application and the dashboard, the sum of the population of the populated places displayed. But beyond that we leverage two more Vue features:

First, we create a Vue method we can call from our templates to format our figures so they are nicely represented with thousand separators and a fixed number of decimals. Look for the formatNumber calls in the HTML and its definition at the Vue application configuration.

The second feature we display is a very interesting one. With Vue you can expose computed variables, that's data that you calculate dynamically. That data is then used on your templates and code as any other Vue data, as they are observed information. Check the computed section in the Vue application and how that avgPop variable is treated like the rest of the normal application data on the HTML template (methods, conditional rendering, etc).


  • Add a conditional class for widgets with no data

In the second step we did a binding between the step variable and the href property of a couple of HTML links. We can do the same with CSS classes to set up the styling of our elements based in different application conditions.

On this example, we added a new noCities boolean computed variable and bind the class disabled to it. The syntax is a bit elaborated but as for now, note that you need to declare an object where the keys are the classes you want to toggle and the values are the conditions for them.

On this exercise the map is centered in Null Island and at a very low zoom level so you'll see the disabled class activated for the different as-box divs of your HTML. Zoom out and as soon as you get any populated place in your bounding box you'll see how that class disappears from your HTML. This is a powerful feature that opens many interesting UI capabilities to your applications.

Binding classes are probably the most common scenario but if you want more complex behavior and even set directly CSS styles on your templates check the documentation.


  • Add a table of features

On this section, we add to the Vue application a new array variable to store the name and population of the features of the bounding box. Check the map.js code to see how we generate a new array using the map JavaScript method. We should always only extract the data we need to the Vue application instead of assigning the full VL object.

We only want to render a few cities on our dashboard so we create a new computed property called topPlaces that will return a sorted array of the most populated places. The number of places return is driven by the application topCitiesCount variable.

Finally, to expose those places on our dashboard we add a new sidebar to the right with an Airship styled table. To loop over the elements of topPlaces and generate a new row each, we use the v-for Vue construct.

Move around the map and check how nicely the table is updated and you can even play on your Vue extension to change the value of topCitiesCount to change the number of cities displayed in your site. Cool, uh?


  • Listen to mouse events
  • Watch a property change

Firstly we add a couple of Airship badges to the tables widget and listen to the click event using v-on:click Vue construct. We can call a Vue method but on this case for simplicity we directly increase or decrease the value of topCitiesCount right away without having to touch any of our JavaScript files. Just look for the badges in the HTML code to find how easy this was.

The second part of this lesson introduces an interesting Vue feature: watchers. If you check the Vue application definition you'll find a new section called watch. We add to this section functions with the same name of a static or computed variable to listen for their change. Anytime a variable changes its value this function is triggered so we can add intentional side effects to changes in our application state. In this case we are setting a zooming variable based in the value of the previous and current zoom levels so we can have -1, 0 or +1 depending if we are zooming out, panning, or zooming in. Finally, we render this variable just aside the zoom level.


  • Move the formula widgets to a dedicated component

If you look at our previous HTML, the code for the formula widgets was a bit repetitive. Anytime we added a new formula we copy&pasted our HTML template and all the Vue logic. If we need to improve the styling or the behavior we should be having quite a few places to touch and adjust. This is a perfect scenario for leveraging Vue components.

Vue components are the framework feature to make pieces of composable markup and behavior. Check on this lesson HTML how we changed our markup to replace our code by a new HTML tag called formula-widget, passing to this tag the minimum information needed to render the logic:

  • The title of our widget
  • A binding (so it's dynamic) to our application variable
  • An optional caption we will see below the title
  • An optional unit to place after the value
  • An optional formatter function to change the way the value is displayed.

The formatter parameter needs to be bind because we can only pass strings to Vue components as static data.

Now you can check the new formula-widget.js file and see a minimal Vue component definition. Any Vue component needs:

  • A name to use as HTML tag in your template
  • A template using the same HTML and Vue constructs as you've seen before
  • An object with the component properties (props), it is optional but recommended to specify your property type and if it is required.
  • Optionally you can also have computed and methods sections as a regular Vue application.

This is not an accurate list and there's much more to discover, check the documentation.

Check how the template resembles our previous repeated code, even in this case we added a few more Airship classes to improve the widget rendering. Check also how the formattedValue computed variable dynamically checks for the formatter existence.


If you followed all the sections at this point you should have a better picture of how to use Vue for your future projects. We covered:

  • Setting up a Vue application
  • Defining variables
  • Defining computed variables
  • Using variables on HTML templates
  • Binding variables to HTML tag attributes
  • Adding methods to the Vue application
  • Conditional rendering
  • Conditional styling
  • Looping arrays on templates
  • Watch for variable changes
  • Attach Vue application logic to DOM events
  • Define a simple Vue component

What's next?

It's highly encouraged to continue taking a look to the official Vue.js guide. This documentation is one of the best examples you'll see on how to do a proper technology onboarding, with clear explanations of the different aspects we've covered here.

In case you need some ideas on what to do next:

  • Can you encapsulate and reuse the table as a component?
  • Move your sidebars to specific components for a more modular application structure
  • Wrap Airship categories or histogram components (be sure to check this section of the official Airship + Vue guide)
  • Listen to Airship components changes to update your layer (tip: you may want to use a computed variable to generate a SQL WHERE or CARTO VL filter expression)
  • Wrap other charting libraries to generate custom widgets (Vega, Vega Lite, Chart.js, Apexcharts, etc.)

Going further, you may want to take a look at:

  • Awesome vue.js is huge compilation of resources, example projects, components, and libraries
  • Using Vue CLI to allow many features like linting, compression, using TypeScript, SASS, etc.
  • Using Vue Router to build a full Single Page Application that offers navigation between some different pages
  • Using Vuex to centralize the storage of your application state