Allowing users to download CSV on click

Another option would be to use the Download component from dash-extensions. Here is a small a data frame example,

import dash
import pandas as pd
import dash_html_components as html

from dash.dependencies import Output, Input
from dash_extensions import Download
from dash_extensions.snippets import send_data_frame

# Example data.
df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [2, 1, 5, 6], 'c': ['x', 'x', 'y', 'y']})
# Create app.
app = dash.Dash(prevent_initial_callbacks=True)
app.layout = html.Div([html.Button("Download", id="btn"), Download(id="download")])

@app.callback(Output("download", "data"), [Input("btn", "n_clicks")])
def func(n_nlicks):
    return send_data_frame(df.to_csv, "mydf.csv", index=False)

if __name__ == '__main__':

This @app.server.route(’/dash/urlToDownload’) is in a different url. To access it, just add to the end of the url. For example:

So, if you want to incorporate it with the app, you can make a button to go to that url.

I got it to work (sort of) with multiple arguments: I can download the correct csv file, and all looks good, but with debug=True it prints an error:

Callback failed: the server did not respond.

Still, it works as intended. What could this error mean?

html.A(dbc.DropdownMenuItem('Button', id='downloadButton'), id='downloadLink'),

    Output('downloadLink', 'href'),
    [Input('downloadButton', 'n_clicks'),
     Input('variable_selector', 'value'),
     Input('measure_selector', 'value'),
     Input('gender_selector', 'value'),
     Input('age_selector', 'value'),
     Input('immdes_selector', 'value'),
     Input('education_selector', 'value'),
     Input('labor_selector', 'value'),
     Input('year_selector', 'value')]
def link_download_map(click, selected_variable, selected_measure, selected_gender, selected_age, selected_heritage, selected_education, selected_labor, selected_year):
    query_params = {'variable': selected_variable,
                    'measure': selected_measure,
                    'gender': selected_gender,
                    'age': selected_age,
                    'heritage': selected_heritage,
                    'education': selected_education,
                    'labor': selected_labor,
                    'year': selected_year
    query_string = urllib.parse.urlencode(query_params, doseq=True)
    return f"/download?{query_string}" 

def download_csv():
    variable = flask.request.args.get('variable')
    measure = flask.request.args.get('measure')
    gender = flask.request.args.get('gender')
    age = flask.request.args.get('age')
    heritage = flask.request.args.get('heritage')
    education = flask.request.args.get('education')
    labor = flask.request.args.get('labor')
    year = flask.request.args.get('year')
    df_out = df[(df['variable'] == variable) & (df['year'] == int(year)) & (df['gender'] == gender) & (df['age'] == age) & (df['immdes'] == immdes) & (df['education'] == education) & (df['labor'] == labor)].copy()

    str_io = io.StringIO()
    df_out.to_csv(str_io, index=False)

    mem = io.BytesIO()
    return flask.send_file(mem,

EDIT: the error occurs with firefox, but neither chrome nor edge

As of Dash 1.20.0, we have an official dcc.Download component :tada:

This component is used just like any other Dash component :slightly_smiling_face: - Update the component’s data property with an Output and the file will be downloaded on the user’s browser.

This is a much nicer alternative than Flask’s app.server.route as it is easier to make dynamic (no query strings or flask.request.args.get!)

Thank you @Emil for contributing this component to dcc :trophy: