How to force dcc.Graph to resize?

I’m building an app that has a dcc.Graph component that starts out in a html.Div which has style={"display": "none"}. I find that when I toggle the style to make the graph show, it doesn’t initially fill the parent container. If I resize the browser window it corrects itself, but the initial size is wrong.

Here’s a minimal example

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
from dash.dependencies import Input, Output

app = dash.Dash()

fig = go.Figure(data=[go.Scatter(x=[1, 2, 3, 4, 5], y=[1, 5, 2, 6, 4])])

app.layout = html.Div(
    [
        html.Button("Show graph", id="button"),
        html.Div(
            id="graph-container",
            children=dcc.Graph(id="graph", figure=fig),
            style={"display": "none"},
        ),
    ],
    style={"max-width": "1000px", "margin": "auto"},
)


@app.callback(
    Output("graph-container", "style"), [Input("button", "n_clicks")]
)
def show_graph(n):
    if n:
        return {"display": "block", "width": "100%"}
    return {"display": "none"}


if __name__ == "__main__":
    app.run_server(debug=True)

Is there a way I can force the dcc.Graph to fill its parent container when the style is toggled?

2 Likes

You can try to set height at the layout of the figure that you will insert into the graph.


@APP.callback(Output('graph', 'figure')

LAYOUT = {'height': <DESIRED>,
          'plot_bgcolor': 'white',
          'paper_bgcolor': 'white'}

figure['layout'].update(LAYOUT)`
1 Like

Thanks Gertoolz, do you know if there’s a way to inherit the height (or in my case, more interested in the width) of the parent container in Dash?

This is the usual behaviour, except in the case where the graph is initially hidden as in my example. I could specify an absolute width using your method but ideally I’d like to match the parent.

Related discussion Plotly resize bug using html.Details / hidden Div and width=100%

Thanks chriddyp. I guess I’ll just set the width to pixel values for now.

Hi, I had a similar issue. I’ve found a quick workaround, not sure if it will help anyone but in case it could I’m sharing it here:

Toy app :

self.app = html.Div([
                        html.Div(className='row',
                                 children=[

                                    html.Div(className='three columns',
                                             id='options_container',
                                             children=[...],
                                             style={'display': 'block'},
                                             ),

                                    html.Div(className='nine columns',
                                             id='charts_container',
                                             children=[

                                                html.Div([
                                                    html.Div([
                                                        html.Button('Settings', id='show_options', n_clicks=1),
                                                        ]),
                                                    html.Div([
                                                        dcc.Graph(id='map'),
                                                    ],),
                                                ], className='row'),

                                                html.Div([
                                                    html.Div([
                                                        dcc.Graph(id='scatter_plot')
                                                    ],),
                                                ], className='row'),

                                            ],
                                            ),
                                 ])
                        ])

Then to get the panel showing/hiding the options as suggested by chriddyp :

        @self.app.callback(
        dash.dependencies.Output('charts_container', 'className'),
        [dash.dependencies.Input('show_options', 'n_clicks')])
    def class_size_button(options_show):
        if options_show % 2 == 0:
            return 'twelve columns'
        else:
            return 'nine columns'

    @self.app.callback(
        dash.dependencies.Output('options_container', 'style'),
        [dash.dependencies.Input('charts_container', 'className')],
        [dash.dependencies.State('show_options', 'n_clicks')])
    def show_hide_options_button(charts_container, options_show):
        if options_show % 2 == 0:
            return {'display': 'none'}
        else:
            return {'display': 'block'}

Using this method, despite the classname of the charts/maps changing correctly to ‘nine columns’, when the panel appeared the svg-container wasn’t changing and keeping the ‘twelve columns’ value, thus filling up too much space. The responsiveness would kick in only if I changed the size of the window and then everything would fall into place.
So what I did is that I added a listener on the button in my charts/maps and each time it is clicked I send again the figure (with the same data) through a callback. Somehow, this way the responsive part is triggered and the figure size is properly recomputed without touching the window.

Something like:

    @self.app.callback(
        dash.dependencies.Output('map', 'figure'),
        [dash.dependencies.Input('show_options', 'n_clicks'),
         ],
        )
    def update_fig_mapbox(show_options):
        
        # If show options is different, then resend the figure