Export Page as HTML

Is it possible to download an entire page as an html file? I’ve attempted to download the page by using the write_html functionality on the layout. I’ve read that this is not possible on GitHub and Stack Overflow, but I was wondering if anyone has achieved this using this or another method?

Also, if not, is it possible to add multiple dropdowns to a figure? I would like to do this to retain the interactivity of the plot. (Don’t feel the need to answer this if you don’t know.)

Thank you!

You should be able to export the (static) HTML layout of a Dash app in a given state. However, interactivity enabled by callbacks (except for clientside callbacks) cannot be exported, as callbacks rely on a Flask server backend.

2 Likes

@Emil Thanks for the reply! I still haven’t been able to find an example of this. Would you happen to know of an example or have a code snippet?

Hi Chris,

I’m not sure if this helps, but I wanted to give users an option to download the interactive Plotly graphs. As Emil mention, this isn’t the interactivity I have included with Dash, but rather the interactivity in the Plotly Figures themselves (with any buttons you included in the FIgures).

Here’s a function that takes a figure list, stores it in the buffer and associates it with a button. While this might not be exactly what you’re looking for, hopefully it’ll be a starting point.

Note: My Dash app has a lot of tabs and I was using this as an optional download for the users. I found that my tabs rendered about 40% faster without this button, and ended up removing it. However, my app has a lot of figures on each tab, and many datapoints per Figure. The extra load time may not be an issue for others. :person_shrugging:

import io
from base64 import b64encode

def create_html_download_button(figs, file_name="plotly_graph", button_name="Download as HTML"):
    """
    Given a Plotly Figure or list of Plotly Figures, stores that in a buffer and returns a button (div.A) object that downloads the figure as an HTML file when clicked.

    :param figs: Plotly Figure or List of Plotly Figures
    :param file_name: String (name of file to download >> '.html' will be appended)
    :param button_name: String (text to display on the button)
    :return: Dash dbc.Button object
    """
    # Special handling of storing multiple figures in buffer.
    if isinstance(figs, list) and len(figs) > 1:
        # keep non-None figs
        figs = [fig for fig in figs if fig != None]
        # Create buffer
        main_buffer = io.StringIO()
        outputs = []
        # Write First figure w/ full HTML and add to list
        _buffer = io.StringIO()
        figs[0].write_html(_buffer, full_html=True, include_plotlyjs='cdn')
        outputs.append(_buffer)
        # Write remaining figs as divs and append to output list
        for fig in figs[1:]:
            _buffer = io.StringIO()
            fig.write_html(_buffer, full_html=False)
            outputs.append(_buffer)

        # CONCAT list into main output
        main_buffer.write(''.join([i.getvalue() for i in outputs]))
    else:
        # Create buffer
        main_buffer = io.StringIO()
        # Write figure to buffer
        if isinstance(figs, list):
            figs[0].write_html(main_buffer)
        else:
            figs.write_html(main_buffer)

    # Convert buffer to bytes and encode/decode
    html_bytes = main_buffer.getvalue().encode()
    encoded = b64encode(html_bytes).decode()
    # CREATE dbc Button
    download_html = dbc.Button(button_name, href="data:text/html;base64," + encoded, download=file_name + ".html")

    # Return the Export HTML button
    return download_html

While multiple dropdowns are possible, they generally don’t interact like most people want them to. To add multiple dropdowns, just add two “buttons” to your updatemenus. Here’s a snapshot of how you might build your updatemenus for this:

updatemenus = [dict(type="dropdown", buttons=your_1st_dropdown, direction='down', active=0, x=.98), dict(type="dropdown", buttons=your_2nd_dropdown, direction='down', x=.88)]
1 Like