We all love Tableau great visualizations but sometimes we need to fetch raw data to print, open in Excel or for any other reason. While viewing Dashboard in Tableau Server, sometimes it’s not clear how to download csv file. There was a hack written by Andy Kriebel ([here]) but setting up was was time consuming and not very flexible. That’s why it’s time to create CSV Download Button using Tableau API capabilities.

How to access the data?

Majority of the solutions for data downloading using Tableau API focuses on embeded dashboards. I would like my user to have seamless experience without him noticing that magic is happening in the backstage. That’s why I will embed CSV Download button directly to dashboard and show it in Tableau Server directly.

Tableau JS API provides pre-defined functions for fetching the data as array: getSummaryDataAsync() (available at dashboard level) and getUnderlyingDataAsync() (available for sheet object). Those are the quivalents of the tabs visible in the “Show Data” dialog available on the dashboard. In this example I will focus on the summary data as this is the most common case and provides aggregated data suitable for most of the users.

To make CSV Download button more flexible, you can specify which sheet visible on dashboard should be downloaded as the text file. Just setup the parameter and you are ready to go.

Required files

For this tutorial I will use 2 external .js libraries:

  • alasql.min.js
  • jquery.min.js

and will reference them directly in the script as:

<script src='alasql.min.js'></script>
<script src='jquery-2.2.4.min.js'></script>

To avoid cross-origin linking, you have to download those .js files and import them to Server. I described Tableau Web connector importing technique in details here Default date slider range with Tableau JS API

Getting things done

The whole csv download script looks like this:

<!DOCTYPE html>

    .btn {
  background: #3498db;
  background-image: -webkit-linear-gradient(top, #3498db, #2980b9);
  background-image: -moz-linear-gradient(top, #3498db, #2980b9);
  background-image: -ms-linear-gradient(top, #3498db, #2980b9);
  background-image: -o-linear-gradient(top, #3498db, #2980b9);
  background-image: linear-gradient(to bottom, #3498db, #2980b9);
  -webkit-border-radius: 28;
  -moz-border-radius: 28;
  border-radius: 28px;
  font-family: Arial;
  color: #ffffff;
  font-size: 18px;
  padding: 10px 20px 10px 20px;
  text-decoration: none;

.btn:hover {
  background: #3cb0fd;
  background-image: -webkit-linear-gradient(top, #3cb0fd, #3498db);
  background-image: -moz-linear-gradient(top, #3cb0fd, #3498db);
  background-image: -ms-linear-gradient(top, #3cb0fd, #3498db);
  background-image: -o-linear-gradient(top, #3cb0fd, #3498db);
  background-image: linear-gradient(to bottom, #3cb0fd, #3498db);
  text-decoration: none;

       <title>Set Worksheet Name you would like to fetch </title>

<script src='alasql.min.js'></script>
<script src='jquery-2.2.4.min.js'></script>
            <script type="text/javascript">

   			var viz;
            var current_sheet;

   		function getTableau() {
		     return parent.parent.tableau;
        function getCurrentViz() {
              return getTableau().VizManager.getVizs()[0];

function gup(name)
            name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
            var regexS = "[\\?&]"+name+"=([^&#]*)";
            var regex = new RegExp( regexS );
            var decoded_uri = decodeURIComponent(window.location.href);
            var results = regex.exec( decoded_uri );
            if( results == null ){
             sheetName = "";
              }  else {
            sheetName= results[1];



function getUnderlyingData(sheetName){
                 viz = getCurrentViz();
                  workbook = viz.getWorkbook();

                options = {
                    maxRows: 0, 
                    ignoreSelection: true,
                    includeAllColumns: false

sheet.getSummaryDataAsync(options).then(function(t) {


function buildMenu(table) {

console.log("entering build menu");
  var columns = table.getColumns();
  var data = table.getData();


  function reduceToObjects(cols, data) {
    var fieldNameMap = $.map(cols, function(col) {
        return col.getFieldName();
    var dataToReturn = $.map(data, function(d) {
      return d.reduce(function(memo, value, idx) {
        memo[fieldNameMap[idx]] = value.value;
        return memo;
      }, {});
    return dataToReturn;

  var niceData = reduceToObjects(columns, data);

   alasql("SELECT * INTO CSV('TableauDataExport.csv',{headers:true}) FROM ?", [niceData]);


<a onClick ="gup('sheetName')" class="btn"> DOWNLOAD CSV FILE </a>
<div id="dataTarget" style="width:100%;height:100%"></div>


This script can be copy-pasted to csv_downloader.html file directly without any changes or you can download the file here:


as you can see there’s a reference to gup() function written by Netlobo [here] which allows to get the variable from the URL. In my case it will be the sheet name I will like to download as csv.

Also it uses Array split technique presented by Robert Rouse [here]

Having html file ready, we need to add it to our viz as webpage. Just pull Web Page object onto dashboard 30 day slider Tableau JS API

For the URL you need to enter – Unfortuntely currently, there’s no easy way to setup this solution for Public/Online

http://servername/webdataconnectors/csv_downloader.html?sheetName=Sheet 1

Changing Sheet 1 in the URL will address different sheet on dashboard. Just remember that Sheet name must be consistent with the Sheet you pulled onto dashboard.CSV Download Button

If everything is correct then you should be able to see clickable CSV Download button. Change Web Page container according to your needs, publish dashboard to server and enjoy functional CSV Download button. Of course, using normal .html syntax, you can edit button to look differently. I just leave it to your imagination.