---
schema_version: "1"
id: e2f8b6af94cef2db5e1c56c55b1306f907f86d502efae6fce0ce810414d9db4b
title: Embedding API v3 Tutorial
collection: tableau
version: current
locale: en-us
portal_url: https://docs.salesforce.com/tableau/current/en-us/api/embedding_api/tutorial/tutorial.md
canonical_url: https://help.tableau.com/current/api/embedding_api/en-us/tutorial/tutorial.htm
source_site: help.tableau.com
content_type: documentation
content_hash: a0d623c57a9f769bc52d4c8f090e10cc087fceb3ccaa8288998dca90f6315f28
---
# Tableau Embedding API v3 Tutorial

This tutorial is a sample web application, created with the Tableau Embedding API v3. It's designed to help you explore and learn about the API using a series of steps that guide you through the basics. To get started, click the button below.

**Note** This tutorial uses the Tableau Embedding API v3 (`tableau.embedding.3.latest.js`). The Tableau Embedding API v3 is a replacement for the Tableau JavaScript API v2. Tableau's Embedding API v3 features a redesigned process for embedding to make your life easier, to modernize the experience, and enable new functionalities.

**Tip** If you are viewing this tutorial on an iPad or tablet, be sure to select website settings for Mobile devices in your browser. If you use Desktop settings, you might be unable to scroll as expected, as taps are considered mouse clicks.

Start the tutorial

The view will load here after you click **Run this code**, below.

[Show .js file](https://help.tableau.com/current/api/embedding_api/en-us/tutorial/tutorial.js "Show entire tutorial.js file")

- [1\. Create](#step1)
- [2\. Filter](#step2)
- [3\. Tabs](#step3)
- [4\. Select](#step4)
- [5\. Promises](#step5)
- [6\. Sheets](#step6)
- [7\. Toolbar](#step7)
- [8\. Events](#step8)

### Create the Viz

As you build your web application the first step is to create, or instantiate the view. You can do this in several ways. The simplest approach is to use the `<tableau-viz>` web component. You can customize the view by setting attributes on the component in your HTML code. For a more dynamic and customizable solution, you can create the view using JavaScript. However, for the most flexibility and greater ease of use, you can combine these two approaches, using the web component and the Embedding API v3 methods. In this tutorial we show you how to do this.

The basic process is as follows:

1. Add a `<tableau-viz id="tableauViz"></tableau-viz>` custom web component in your HTML code.
2. In your JavaScript code, retrieve the element using `viz = document.getElementById("tableauViz")`. This returns an instance of the `TableauViz` class.
3. Provide configuration options such as `viz.hideTabs` and `viz.hideToolbar`.
4. Set the `viz.src` property to your workbook URL.

Here's the JavaScript code:

function initializeViz() {
  viz = document.getElementById("viz");
  viz.width = 1200;
  viz.height = 800;
  viz.hideTabs = true;
  viz.hideToolbar = true;

  const onFirstInteractive = () => {
    workbook = viz.getWorkbook();
    activeSheet = workbook.getActiveSheet();
  };

  viz.addEventListener(TableauEventType.FirstInteractive, onFirstInteractive);
  viz.src = "https://public.tableau.com/views/WorldIndicators\_17297174004850/GDPpercapita";
}     

Run this code

You should now see a view with which you can interact, just like you can with views on Tableau.

In the code above, setting the `viz.src` property handles loading the view. Adding a `FirstInteractive` event listener allows you to perform actions once the view has finished loading. In this case, the function caches the `workbook` and `activeSheet` variables so they can be used later on. These two variables were declared as global variables [in the actual script](https://help.tableau.com/current/api/embedding_api/en-us/tutorial/tutorial.js). Typically you'll want to create the view when the page has finished loading and the browser is ready. If you're using jQuery, this can be done using jQuery's ready handler:

$(initializeViz);

Next

### Filter Values

Filtering is a core capability of Tableau. In the view above, there are already Region and Year quick filter controls, but you can use the API to more tightly control what gets filtered. This is done using the `applyFilterAsync` method on a `Worksheet` object, which was cached in the `activeSheet` variable in step 1. Note that the `applyFilterAsync` method takes an array of filter values. You need to use square `[]` brackets to enclose the values even if there is only a single value.

The following code filters the "Region" field to show only "The Americas":

async function filterSingleValue() {
  await activeSheet.applyFilterAsync(
    "Region",
    \["The Americas"\],
    FilterUpdateType.Replace);
}     

Run this code

You should now see that the view is filtered and only "The Americas" check box is selected under the Region quick filter.

The next code sample shows you how to add two more values to the filter, using the same syntax but instead specifying an array of values instead of a single value. Also note that `FilterUpdateType.Add` is used instead of `FilterUpdateType.Replace`, which instructs the view to add additional criteria to the filter instead of replacing the values that are currently filtered:

async function addValuesToFilter() {
  await activeSheet.applyFilterAsync(
    "Region",
    \["Europe", "Middle East"\],
    FilterUpdateType.Add);
}     

Run this code

Similarly, you can remove values from the filter by using `FilterUpdateType.Remove`:

async function removeValuesFromFilter() {
  await activeSheet.applyFilterAsync(
    "Region",
    \["Europe"\],
    FilterUpdateType.Remove);
}     

Run this code

You can also include all values in the filter by using `FilterUpdateType.All`.

The filters you've seen so far have all had associated quick filters in the view. However, you can also create new filters. For example, you can create a filter for the x-axis, the "F: GDP per capita (curr $)" field, and specify that you only want to see countries where the GDP is greater than $40K, but less than $60K. To do this, you use the `applyRangeFilter` method, using a range of values as the criteria:

async function filterRangeOfValues() {
  await activeSheet.applyRangeFilterAsync(
    "F: GDP per capita (curr $)",
    {
      min: 40000,
      max: 60000,
    },
    FilterUpdateType.Replace
  );
}     

Run this code

Finally, you can clear the filters. For example:

async function clearFilters() {
  await activeSheet.clearFilterAsync("Region");
  await activeSheet.clearFilterAsync("F: GDP per capita (curr $)");
}     

Run this code

Back Next

### Switch Tabs

Sometimes a single sheet in a workbook doesn't convey all of the information that you'd like your user to see. You can use the API to switch from the current sheet to another published sheet within the same workbook (note that the workbook must have been published to the server with [Show Sheets as Tabs](https://onlinehelp.tableau.com/current/pro/online/en-us/help.htm#publish_workbooks_howto.htm) enabled). To switch sheets, you use the `activateSheetAsync` method on a `Workbook` object, which was cached in a global `workbook` variable in step 1. Here's how you switch the sheet to a map worksheet named "GDP per capita map".

async function switchToMapTab() {
  await workbook.activateSheetAsync("GDP per capita map");
}     

Run this code

Back Next

### Select Values

Filtering a view is useful when you want to focus the user's attention on a specific set of values by removing all other values not matching the filter criteria. However, sometimes it's useful to select values instead. This still focuses the user's attention on specific values, but the context of other values remains in the view. To do this, you use the `selectMarksAsync` method. The syntax is very similar to the `applyFilterAsync` method that you used previously. For example, the following code selects all of the marks in the "Asia" region:

async function selectSingleValue() {
  const selections = \[
    {
      fieldName: "Region",
      value: "Asia",
    },
  \];

  await workbook.activeSheet.selectMarksByValueAsync(selections, SelectionUpdateType.Replace);
}     

Run this code

The only change between the code above and the filter code you used earlier is that `SelectionUpdateType` was specified instead of `FilterUpdateType`. Also, notice that `workbook.activeSheet` is used instead of the `activeSheet` global variable because the sheets were switched in step 3 and the global variable wasn't updated to point to the new active sheet.

In the following code sample, Africa and Oceania are added to the previous selection:

async function addValuesToSelection() {
  const selections = \[
    {
      fieldName: "Region",
      value: \["Africa", "Oceania"\],
    },
  \];

  await workbook.activeSheet.selectMarksByValueAsync(selections, SelectionUpdateType.Add);
}     

Run this code

Again, the code should look familiar since the syntax is almost identical to filtering. At this point, you've selected Asia, Africa, and Oceania. The next code sample will demonstrate how to remove selections. In this case, you will remove countries that have a GDP less than $5,000. To do this, you use a range just like you did for filtering, except you'll only specify a max value:

async function removeFromSelection() {

  const selections = \[
    {
      fieldName: "AVG(F: GDP per capita (curr $))",
      value: {
        min: 0,
        max: 5000,
      },
    },
  \];

  await workbook.activeSheet.selectMarksByValueAsync(selections, SelectionUpdateType.Remove);
}     

Run this code

Clearing the selection is just as easy as clearing a filter, using the `clearSelectedMarksAsync` method:

async function clearSelection() {
  await workbook.activeSheet.clearSelectedMarksAsync();
}      

Run this code

Back Next

### Promises

The Tableau JavaScript API uses [JavaScript Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), allowing you to use async/await syntax to pause execution until asynchronous operations complete. Each method that ends with `Async` returns a `Promise` object.

The following code sample demonstrates how you can use some of the methods you've learned thus far to execute multiple commands. First you switch to the "GDP per capita by region" sheet. After that has finished, you apply a range filter. Once Tableau has applied the filter, you select some marks.

async function switchTabsThenFilterThenSelectMarks() {
  const newSheet = await workbook.activateSheetAsync("GDP per capita by region");
  activeSheet = newSheet;

  // It's important to await the promise so the next step
  // won't be called until the filter completes.
  await activeSheet.applyRangeFilterAsync(
    "Date (year)",
    {
      min: new Date(Date.UTC(2002, 1, 1)),
      max: new Date(Date.UTC(2008, 12, 31)),
    },
    FilterUpdateType.Replace
  );

  const selections = \[
    {
      fieldName: "AGG(GDP per capita (weighted))",
      value: {
        min: 20000,
        max: Infinity,
      },
    },
  \];

  return await activeSheet.selectMarksByValueAsync(selections, SelectionUpdateType.Replace);
}     

Run this code

Before moving on to the next step, let's take a look at how errors are handled. The code below intentionally causes an error to happen by providing an invalid parameter to the `applyFilterAsync` method:

async function triggerError() {
  await workbook.activateSheetAsync("GDP per capita by region");

  // Do something that will cause an error.
  try {
    await activeSheet.applyFilterAsync("Date (year)", \[2008\], "invalid");
  } catch (err) {
    alert(\`We purposely triggered this error to show how error handling happens.\\n\\n${err}\`);
  }
}     

Run this code

Back Next

### Work with Sheets

Workbooks created in Tableau Desktop contain worksheets and, sometimes, one or more dashboards. The dashboards themselves typically contain one or more worksheets. This is why, in the API, the concept of "sheet" encompasses both worksheets and dashboards. Worksheet and dashboard objects do not have the same set of actions, however. Worksheets are the only entry point for acting on both worksheet and dashboard objects. You can't act directly on a dashboard object.

The code samples below illustrate how this works. The first code sample demonstrates how you would query all of a workbook's sheets. After you click **Run this code** the dialog that appears lists workbook's sheets:

function querySheets() {
  const sheets = workbook.publishedSheetsInfo;
  let text = getSheetsAlertText(sheets);
  text = \`Sheets in the workbook:\\n${text}\`;
  alert(text);
}    

Run this code

Here's how you would query worksheets in a dashboard. Notice that the filter is still applied to the "GDP per region" worksheet in the dashboard, but the marks are not selected:

async function queryDashboard() {
  // Notice that the filter is still applied on the "GDP per capita by region"
  // worksheet in the dashboard, but the marks are not selected.
  const dashboard = await workbook.activateSheetAsync("GDP per Capita Dashboard");
  const worksheets = dashboard.worksheets;
  let text = getSheetsAlertText(worksheets);
  text = "Worksheets in the dashboard:\\n" + text;
  alert(text);
}       

Run this code

You'll notice that there are scrollbars on the viz. This is because the fixed size specified while initializing the `viz` (in step 1) is different than the fixed size specified for this dashboard by the workbook author. To see the entire dashboard, you can change the size behavior to `Automatic`, which tells the viz to fit the available space. This removes the scrollbars at the expense of making each `Worksheet` in the dashboard slightly smaller.

async function changeDashboardSize() {
  const dashboard = await workbook.activateSheetAsync("GDP per Capita Dashboard");
  await dashboard.changeSizeAsync({
    behavior: SheetSizeBehavior.Automatic,
  });
}       

Run this code

Now, here's how you select filters and change settings on multiple sheets within a dashboard. The code sample applies to a dashboard with two worksheets:

async function changeDashboard() {
  const dashboard = await workbook.activateSheetAsync("GDP per Capita Dashboard");
  const mapSheet = dashboard.worksheets.find((w) => w.name === "Map of GDP per capita");
  const graphSheet = dashboard.worksheets.find((w) => w.name === "GDP per capita by region");

  await mapSheet.applyFilterAsync("Region", \["Middle East"\], FilterUpdateType.Replace);

  // Do these two steps in parallel since they work on different sheets.
  await Promise.all(\[
    mapSheet.applyFilterAsync("YEAR(Date (year))", \[2010\], FilterUpdateType.Replace),
    graphSheet.clearFilterAsync("Date (year)"),
  \]);

  const selections = \[
    {
      fieldName: "Region",
      value: "Middle East",
    },
  \];

  return await graphSheet.selectMarksByValueAsync(selections, SelectionUpdateType.Replace);
}       

Run this code

Back Next

### Control Toolbar Commands

Tableau toolbar commands are available from the `TableauViz` object, which is the highest level object. Some commands act on the entire worksheet or dashboard, and some act on only the selected zone. Export PDF and Export Image act on the entire worksheet or dashboard. Here's the code for Export PDF:

async function exportPDF() {
  await viz.displayDialogAsync(TableauDialogType.ExportPDF);
}       

Run this code

And here's the code for Export Image:

async function exportImage() {
  await viz.exportImageAsync();
}       

Run this code

Unlike the Export PDF and Export Image commands, which apply to the entire worksheet or dashboard, the commands Export as Crosstab and Export Data apply to the currently selected zone. To see this for yourself, select a mark or marks in the above view and then run the code, you'll see that just the data for the selected marks is presented for export. The code for Export as CSV is as follows:

async function exportCrossTab() {
  await viz.displayDialogAsync(TableauDialogType.ExportCrossTab);
}       

Run this code

When there aren't parameters specified for Export as Crosstab or Export Data, the currently selected zone is exported. You can also specify a sheet name or you can pass in a sheet object. Here's the code for Export Data:

async function exportData() {
  await viz.displayDialogAsync(TableauDialogType.ExportData);
}      

Run this code

Finally, the Revert All command restores the workbook to its original, published state:

async function revertAll() {
  await workbook.revertAllAsync();
}      

Run this code

Back Next

### Listen for Events

The operations you've done thus far controlled the viz from the outside in. One of the most powerful capabilities of the Tableau Embedding API v3 is that it also gives you the ability to respond to interactions that your user performs on the view. In other words, it lets you respond to events that occur from the inside out.

Listening to events is done similarly to how you listen to events in the browser. The `addEventListener` method on the `TableauViz` class lets you register a callback function when a specific event occurs in the view. For example, you can listen to the `MarksSelection` event, which gets raised whenever new marks become selected:

function listenToMarksSelection() {
  viz.addEventListener(TableauEventType.MarkSelectionChanged, onMarksSelection);
}

async function onMarksSelection(marksEvent) {
  const marks = await marksEvent.detail.getMarksAsync();
  reportSelectedMarks(marks);
}

export function reportSelectedMarks(marks) {
  let html = \[\];
  marks.data.forEach((markDatum) => {
    const marks = markDatum.data.map((mark, index) => {
      return {
        index,
        pairs: mark.map((dataValue, dataValueIndex) => {
          return {
            fieldName: markDatum.columns\[dataValueIndex\].fieldName,
            formattedValue: dataValue.formattedValue,
          };
        }),
      };
    });

    marks.forEach((mark) => {
      html.push(\`<b>Mark ${mark.index}:</b><ul>\`);

      mark.pairs.forEach((pair) => {
        html.push(\`<li><b>fieldName:</b> ${pair.fieldName}\`);
        html.push(\`<br/><b>formattedValue:</b> ${pair.formattedValue}</li>\`);
      });

      html.push("</ul>");
    });
  });

  const dialog = $("#dialog");
  dialog.html(html.join(""));
  dialog.dialog("open");
}     

Run this code

Click **Run this code** above, then manually select some marks in the view. A dialog will appear that describes each of the marks you selected.

To stop listening to the event, you call the `removeEventListener` method, passing the same function that you specified in `addEventListener`:

function removeMarksSelectionEventListener() {
  viz.removeEventListener(TableauEventType.MarkSelectionChanged, onMarksSelection);
}     

Run this code

Click **Run this code** again, then select some marks. Because `removeEventListener` was called you are not notified, and the dialog doesn't appear.

This tutorial has covered the basics on how to integrate Tableau views into your own web applications. For more in-depth explanations, see the [Tableau Embedding API v3](https://help.tableau.com/current/api/embedding_api/en-us/index.html) documentation, as well as the [Embedding API v3 Reference](https://help.tableau.com/current/api/embedding_api/en-us/reference/).

Back