Embedding maps

Maps built with CARTO can be easily embedded in other websites or applications, using an <iframe> HTML element. This is a great way for your maps to make a larger impact by reaching a larger audience, both outside and inside your organization.

Additionally, embedding a map can be a way to create beautiful data stories by wrapping your map with richer, interactive context and storytelling in a presentation or a website.

Example of an embedded map with interactive controls via URL parameters

Embedding a map

To embed a map, simply click on "Share" as seen in Publishing and sharing maps, and copy the embed code available in the sharing dialog. The resulting code should be similar to this one.

<iframe 
    width="100%" 
    height="640px" 
    src="https://clausa.app.carto.com/map/ff76a0cd-fd9c-4893-8c2b-d9c587f2d699"></iframe>

Public embedding

Maps that are shared as Public can be embedded anywhere without restrictions. Public maps protected with a password can also be embedded, forcing viewers to know and introduce the password.

Private embedding

You can securely limit who can view your embedded map by sharing it exclusively with your Organization, specific Users/Groups, or just yourself as Private.

Then, when you embed a non-public map, there are different strategies to verify and control if the external application is allowed to load the embedded map:

Method 1: using the Map API Access Token

The Map API Access Token is a long-lived, read-only API Access Token automatically generated for each map. This is the simplest method for embedding private maps in external applications.

How it works:

  1. Navigate to your map's sharing settings in the CARTO interface and copy the Map API Access Token displayed in the UI.

  2. You can then pass this token in your app via either:

    1. URL parameter: Include the token directly in your iframe URL as a token query parameter.

    2. PostMessage flow (recommended):

      1. Initialize your iframe with a use-external-access-token query parameter.

      2. The embedded map will request the token via a postMessage event with eventType cartoAccessTokenRequest instead of expecting it in the URL.

        1. Make sure you verify the event.origin of this message.

      3. Respond to this message with a cartoAccessToken event type, where the event data is the Map API Access Token.

        1. Make sure you set the targetOrigin so that this message is only received by the CARTO iframe.

Example (URL Parameter)
<iframe
  src="https://your-carto-domain.com/map/your-map-id?use-external-access-token&token=YOUR_MAP_API_ACCESS_TOKEN"
  width="100%"
  height="600px">
</iframe>
Example (PostMessage flow)
<iframe 
  id="cartoMap"
  src="https://your-carto-domain.app.carto.com/map/your-map-id?use-external-access-token"
  width="100%" 
  height="600px">
</iframe>

<script>
  // Your Map API Access Token from CARTO settings
  const MAP_API_TOKEN = 'YOUR_MAP_API_ACCESS_TOKEN';
  const iframe = document.getElementById('cartoMap');

  // Listen for token requests from the embedded map
  window.addEventListener('message', (event) => {
    // Verify the origin for security
    if (event.origin !== 'https://your-carto-domain.app.carto.com') return;
         
    // Respond to token request
    if (event.data.type === 'cartoAccessTokenRequest') {
      iframe.contentWindow.postMessage({
        type: 'cartoAccessToken',
        data: MAP_API_TOKEN
      }, event.origin);
    }
  });
</script>

Method 2: using an SPA OAuth Flow

SPA OAuth Clients are an authentication mechanism that generate short-lived tokens that represent a specific user's permissions, including access to specific maps. This is great if your application already makes authenticated calls to CARTO using these user-level tokens.

How it works:

  1. Implement the recommended authentication flow with an SPA OAuth Client (where the user actively inputs their CARTO credentials), until you obtain a valid access token.

  2. Make sure that the user has access to the embedded map, by sharing this map with them.

  3. Use the PostMessage flow to pass the resulting OAuth Access Token to the embedded CARTO map:

    1. Initialize your iframe with a use-external-access-token query parameter.

    2. The embedded map will request the token via a postMessage event with eventType cartoAccessTokenRequest instead of expecting it in the URL.

      1. Make sure you verify the event.origin of this message.

    3. Respond to this message with a cartoAccessToken event type, where the event data is the OAuth Access Token.

      1. Make sure you set the targetOrigin so that this message is only received by the CARTO iframe.

Example (PostMessage flow)
  <iframe 
    id="cartoMap"
    src="https://your-carto-domain.app.carto.com/map/your-map-id?use-external-access-token"
    width="100%" 
    height="600px">
  </iframe>

  <script>
  // After OAuth authentication with SPA, you'll get an access token
  const userAccessToken = await authenticateUser(); // Your OAuth implementation
  const iframe = document.getElementById('cartoMap');

  // Listen for token requests from the embedded map
  window.addEventListener('message', (event) => {
    // Verify origin for security
    if (event.origin !== 'https://your-carto-domain.com') return;

    if (event.data.type === 'cartoAccessTokenRequest') {
      // Respond with user's OAuth access token
      iframe.contentWindow.postMessage({
        type: 'cartoAccessToken',
        data: userAccessToken
      }, event.origin);
    }
  });
  </script>

Method 3: re-using an existing login session

If no token URL parameter is used, and the postMessage cartoAccessToken flow is not completed, CARTO will fall back to re-using the existing login session in the browser, if it exists. This will work when the following requirements are met.

  • Users must be previously logged-in to CARTO in the same browser as the embedded map

  • Users must have access to the map (for example, they need to be part of the group that map is shared with)

  • CARTO needs to be able to access cookies in the user browser. Learn more in the known limitations section of this page.

If the user has access but is not currently logged-in to CARTO, they will need to login in a separate tab/window and refresh the page containing the embedded map.

Interacting with embedded maps

Users can interact with the embedded map in the same way they would in a standalone window by engaging with the map layers, widgets, SQL parameters...

An additional way to interact with the embedded map is by modifying the URL parameters. This is particularly interesting because it allows the parent application to control the state of the embedded CARTO map.

By modifying dynamically URL parameters in your embedded map, you can, for example:

  • Control the map zoom and camera from user controls in your parent application

  • Modify the map filters (widgets and parameters) when the users selects different options in your parent application

  • Show/hide layers based on the user preferences in your parent application

Listening to events from embedded maps

When embedding CARTO maps in an iframe, you can listen to events emitted by the embedded map to respond to user interactions and map state changes. This allows you to create bi-directional interactive experiences. The map uses the postMessage API to communicate with the parent application.

cartoMapLoaded

This event is fired once when the map finishes loading and is ready to use.

  • Event Type: cartoMapLoaded

Event Data reference
{
  // The title of the map
  "title": "string",
  
  // List of widgets available on the map
  "widgets": [
    {
      "id": "string",        // Widget identifier
      "type": "string",      // Widget type: "category" | "pie" | "histogram" | "range"
      "title": "string"      // Widget title
    }
  ],
  
  // List of SQL parameters defined in the map
  "sqlParameters": [
    {
      "name": "string",      // Parameter name
      "type": "string"       // Parameter type: "category" | "numeric" | "dateRange" | "numericRange"
    }
  ]
}
Javascript Example
window.addEventListener('message', (event) => {
  if (event.data.type === 'cartoMapLoaded') {
    console.log('Map loaded:', event.data.data.title);
    console.log('Available widgets:', event.data.data.widgets);
    console.log('SQL parameters:', event.data.data.sqlParameters);
  }
});

cartoMapUpdated

This event is fired whenever the map state changes (viewport changes, layer visibility, filters, etc.). This normally happens when the user zooms in, uses a widget or a SQL parameter, etc.

  • Event Type: cartoMapUpdated

Event Data reference
{
  "lat": number,              // Map center latitude
  "long": number,             // Map center longitude
  "zoom": number,             // Current zoom level
  "bearing": number,          // Map bearing/rotation in degrees
  "pitch": number,            // Map pitch/tilt in degrees
  "search": "string",         // (Optional) Current geocoder search text
  "layers": [number],         // Indices of visible layers
  "mask": "string",           // (Optional) WKT representation of the spatial filter geometry
  
  // Active widget filters
  "widgetFilters": [
    {
      "id": "string",         // Widget identifier
      "value": []             // string[] for category/pie widgets | number[][] for range/histogram, which is an array of [min, max] pairs
    }
  ],
  
  // Current SQL parameter values
  "sqlParametersValues": [
    {
      "name": "string",       // Parameter name
      "value": any            // string | number | string[] depending on parameter type
    }
  ]
}
Javascript Example
window.addEventListener('message', (event) => {
  if (event.data.type === 'cartoMapUpdated') {
    const mapState = event.data.data;
    console.log('Map position:', mapState.lat, mapState.long, mapState.zoom);
    console.log('Active widget filters:', mapState.widgetFilters);
    console.log('SQL parameter values:', mapState.sqlParametersValues);
  }
});

Known limitations

  1. The parent application must specify a valid origin HTTP header for the CARTO embedded map to load. This is a security requirement.

  2. For private embedding when re-using existing login session (method 3): If the user browser does not allow access to cookies to the CARTO embedded iframe, a screen will pop up, prompting for permission.

    • Some browsers such as Firefox or Brave offer advanced privacy/tracking protection that might interfere with the CARTO embedded iframe. While these features are great for user privacy, they also disable legitimate secure use cases like this one. Users can disable these protections.

Last updated

Was this helpful?