✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
⚾️ It's finally Baseball season! Root for the home team... & Register for our Sports Analytics Webinar!

Clean way to download table data

I’m trying to find a clean way to add download links to tables I’ve generated. Here’s some sample code (using this post as a baseline example):

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pandas as pd
import flask
from StringIO import StringIO

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table_experiments as dt
from dash.dependencies import Input, Output

DF = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')
DFS = {'2002': DF[DF['year'] == 2002],
       '2007': DF[DF['year'] == 2007],

def tabbed_tables():
    table1 = dt.DataTable(rows=DFS['2002'].to_dict('records'),
    button = html.Button('Download', id='download-button-0', value='table-0')
    link1 = dcc.Link(button, href='/', id='download-link-0')

    table2 = dt.DataTable(rows=DFS['2007'].to_dict('records'),
    button = html.Button('Download', id='download-button-1', value='table-1')
    link2 = dcc.Link(button, href='/', id='download-link-1')

    tab1 = dcc.Tab(label="2002", children=[table1, link1], id='table-0-tab')
    tab2 = dcc.Tab(label="2007", children=[table2, link2], id='table-1-tab')

    return dcc.Tabs(id="tabs", children=[tab1, tab2],
                    style={'fontFamily': 'system-ui'},
                      'borderLeft': '1px solid #d6d6d6',
                      'borderRight': '1px solid #d6d6d6',
                      'borderBottom': '1px solid #d6d6d6',
                      'padding': '44px'

app = dash.Dash()

app.layout = html.Div(tabbed_tables(), style={
                 'width': '100%',
                 'maxWidth': '1200px',
                 'margin': '0 auto'

    Output('download-link-0', 'href'),
    [Input('download-button-0', 'n_clicks')])
def trigger_table_1_download_link(n_clicks):
    # Don't trigger when loading page the first time
    if n_clicks == "None":
            return ''

    return '/download?value={}'.format('2002')

    Output('download-link-1', 'href'),
    [Input('download-button-1', 'n_clicks')])
def trigger_table_2_download_link(n_clicks):
    # Don't trigger when loading page the first time
    if n_clicks == "None":
            return ''

    return '/download?value={}'.format('2007')

def download_csv():
    value = flask.request.args.get('value')
    print("Downloading table ", value)
    df = DFS[value]

    buff = StringIO()
    df.to_csv(buff, encoding='utf-8')
    return flask.send_file(buff,
                           attachment_filename=value + '.csv',

if __name__ == '__main__':

The one piece of functionality I’m missing is that It doesn’t download a file. When I click the button on the first tab, I see /download?value=2002 appended to the url, but it doesn’t actually follow through. I have to click on my address bar and hit enter to follow the link and download the file. I’ve tried adding dcc.Location(id='url', refresh=True) to my layout, but that doesn’t trigger the download. Any ideas?

There’s also a few cleanliness issues I have with this approach:

  1. Ideally, there should only be one callback defined, but I’d have to be able to parametrize the component_ids of the Inputs and Outputs. Is that possible? I remember reading that callbacks can’t be generated at runtime.
  2. It’d be nice to not have to generate a URL and go through a route to download a file. Is that possible?

I hope this helps Download raw data - #7 by carlottanegri

Even better and cleaner alternative: Show and Tell - Download Component

There are also data table alternative which implement a download button such as

from data_table import DataTable


from dash_tabulator import DashTabulator


If you render the table anyway you could use those solutions instead of

import dash_table_experiments as dt