Panel 1.4 has just been released! Checkout the release notes and support Panel by giving it a 🌟 on Github.


Open this notebook in Jupyterlite | Download this notebook from GitHub (right-click to download).

import panel as pn

The FileDownload widget allows downloading a file on the frontend by sending the file data to the browser either on initialization (if embed=True) or when the button is clicked.

Discover more on using widgets to add interactivity to your applications in the how-to guides on interactivity. Alternatively, learn how to set up callbacks and (JS-)links between parameters or how to use them as part of declarative UIs with Param.


For details on other options for customizing the component see the layout and styling how-to guides.


  • auto (boolean): Whether to download the file the initial click (if True) or when clicking a second time (or via the right-click Save file menu).

  • callback (callable): A callable that returns a file or file-like object (takes precedence over file if set).

  • embed (boolean): Whether to embed the data on initialization.

  • file (str, Path or file-like object): A path to a file or a file-like object.

  • filename (str): The filename to save the file as.


  • button_style (str): The button style, either ‘solid’ or ‘outline’.

  • button_type (str): A button theme; should be one of 'default' (white), 'primary' (blue), 'success' (green), 'info' (yellow), 'light' (light), or 'danger' (red)

  • icon (str): An icon to render to the left of the button label. Either an SVG or an icon name which is loaded from

  • icon_size (str): Size of the icon as a string, e.g. 12px or 1em.

  • label (str): A custom label for the download button (by default uses the filename)

  • name (str): The title of the widget

The FileDownload widget accepts a path to a file or a file-like object (with a .read method) if the latter is provided a filename must also be set. By default (auto=True and embed=False) the file is only transferred to the browser after the button is clicked (this requires a live-server or notebook kernel):

file_download = pn.widgets.FileDownload(file='FileDownload.ipynb', filename='custom_filename.ipynb')


The file data may also be embedded immediately using embed parameter, this allows using the widget even in a static export:

pn.widgets.FileDownload(file='FileDownload.ipynb', embed=True)
Traceback (most recent call last):
  File "/Users/runner/work/panel/panel/panel/io/", line 183, in exec_with_return
    out = eval(compile(_convert_expr(last_ast.body[0]), "<ast>", "eval"), global_context)
  File "<ast>", line 1, in <module>
  File "/Users/runner/work/panel/panel/panel/widgets/", line 159, in __init__
  File "/Users/runner/miniconda3/envs/test-environment/lib/python3.11/site-packages/param/", line 53, in _depends
    return func(*args, **kw)
  File "/Users/runner/work/panel/panel/panel/widgets/", line 269, in _transfer
  File "/Users/runner/work/panel/panel/panel/widgets/", line 214, in _sync_data
    raise FileNotFoundError('File "%s" not found.' % fileobj)
FileNotFoundError: File "FileDownload.ipynb" not found.

If auto=False is set the file will not be downloaded on the initial click but will change the label from “Transfer ” to “Download ” once the data has been synced. This offers an opportunity to download using the Save as dialog once the data has been transferred.

    file='FileDownload.ipynb', button_type='success', auto=False,
    embed=False, name="Right-click to download using 'Save as' dialog"

The FileDownload widget may also be given a file-like object, e.g. here we save a pandas DataFrame as a CSV to a StringIO object and pass that to the widget:

from bokeh.sampledata.autompg import autompg

from io import StringIO
sio = StringIO()

pn.widgets.FileDownload(sio, embed=True, filename='autompg.csv')

If you want to generate the file dynamically, e.g. because it depends on the parameters of some widget you can also supply a callback (which may be decorated with the widgets and/or parameters it depends on):

years_options = list(autompg.yr.unique())
years = pn.widgets.MultiChoice(
    name='Years', options=years_options, value=[years_options[0]], margin=(0, 20, 0, 0)
mpg = pn.widgets.RangeSlider(
    name='Mile per Gallon', start=autompg.mpg.min(), end=autompg.mpg.max()

def filtered_mpg(yrs, mpg):
    df = autompg
    if years.value:
        df = autompg[autompg.yr.isin(yrs)]
    return df[(df.mpg >= mpg[0]) & (df.mpg <= mpg[1])]

def filtered_file(yr, mpg):
    df = filtered_mpg(yr, mpg)
    sio = StringIO()
    return sio

fd = pn.widgets.FileDownload(
    callback=pn.bind(filtered_file, years, mpg), filename='filtered_autompg.csv'

    pn.Row(years, mpg),
    pn.panel(pn.bind(filtered_mpg, years, mpg), width=600),


The color of the FileDownload button can be set by selecting one of the available button_type values and the button_style can be 'solid' or 'outline':

    *(pn.Column(*(pn.widgets.FileDownload(button_type=p, button_style=bs) for p in pn.widgets.Button.param.button_type.objects))
    for bs in pn.widgets.Button.param.button_style.objects)


Like other buttons you can provide an explicit icon, either as a named icon loaded from

    pn.widgets.FileDownload(icon='alert-triangle-filled', button_type='warning', file='FileDownload.ipynb'),
    pn.widgets.FileDownload(icon='bug', button_type='danger', file='FileDownload.ipynb')

or as an explicit SVG:

cash_icon = """
<svg xmlns="" class="icon icon-tabler icon-tabler-cash" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
  <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
  <path d="M7 9m0 2a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2z" />
  <path d="M14 14m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
  <path d="M17 9v-2a2 2 0 0 0 -2 -2h-10a2 2 0 0 0 -2 2v6a2 2 0 0 0 2 2h2" />

pn.widgets.FileDownload(icon=cash_icon, button_type='success', icon_size='2em', file='FileDownload.ipynb')

Open this notebook in Jupyterlite | Download this notebook from GitHub (right-click to download).