plotly.py icon indicating copy to clipboard operation
plotly.py copied to clipboard

Inconsistent theme updates using `template`

Open mykolaskrynnyk opened this issue 6 months ago • 2 comments

According to Theming and templates in Python, a figure can be created using a predefined template or updated to match it. However, the way updates happen is confusing if not inconsistent. Consider the examples below for plotly[express] == 6.2.0.

Plotly Express

import plotly.express as px

df = px.data.iris()

# uses the default `plotly` template
fig = px.scatter(df, x="sepal_width", y="sepal_length", color="petal_length", title="Figure 1")
fig.show()

# update to use another template, note that the colour scale is unchanged
fig.update_layout(template="seaborn", title="Figure 2")
fig.show()

# this is not equivalent to Figure 2
fig = px.scatter(df, x="sepal_width", y="sepal_length", color="petal_length", template="seaborn", title="Figure 3")
fig.show()

So Figure 3 is not the same as Figure 2, at least because the colour scales don't match. For custom themes, I can imagine that there may be other components of the figure object that would differ. As far as I know, there is no way to update traces using a template, e.g., fig.update_traces(template="seaborn"), to adjust non-layout settings.

Plotly Graph Object

To make things more confusing, there is a Specifying themes in graph object figures example in the documentation that does the same, but it actually works as expected. Adjusting the example we get:

import plotly.express as px
import plotly.graph_objects as go

df = px.data.iris()

# uses the default `plotly` template
fig = go.Figure(
    data=go.Scatter(
        x=df["sepal_width"],
        y=df["sepal_length"],
        mode="markers",
        marker={
            "color": df["petal_length"],
            "showscale": True,
        },
    )
)
fig.update_layout(title="Figure 4")
fig.show()

# update to use another template, note that the colour scale has changed as expected
fig.update_layout(template="seaborn", title="Figure 5")
fig.show()

However, specifying an explicit colorscale will prevent a template update from setting its own colour scheme:

# same as Figure 4 but with an explicit `colorscale`
fig = go.Figure(
    data=go.Scatter(
        x=df["sepal_width"],
        y=df["sepal_length"],
        mode="markers",
        marker={
            "color": df["petal_length"],
            "colorscale": "Viridis",  # or any other colour scale
            "showscale": True,
        },
    )
)
fig.update_layout(title="Figure 6")
fig.show()

# no longer matches Figure 5
fig.update_layout(template="seaborn", title="Figure 7")
fig.show()

Suggestions

  1. I would expect fig.update_layout(template="seaborn") to have the same effect, regardless of whether px or go interface is used. From this perspective, to result for Figure 2 is a bug (the colour scale should have changed for seaborn template).
  2. When an existing figure is updated, the settings from a template should take precedence over any existing settings. So Figure 5 and Figure 7 should produce the same result. This would entail that an update in Figure 7 overwrites whatever the colorscale setting Figure 6 has with that of a template provided in update_layout.

mykolaskrynnyk avatar Jul 10 '25 09:07 mykolaskrynnyk

Encountering the same thing. After doing some digging I couldn't immediately find out why this is happening and it seems counterintuitive. A workaround for you in the express case may be to pass through the template as an argument in the construction of the figure, this worked for us.

john-ramsey avatar Sep 09 '25 10:09 john-ramsey

Agree this is very counterintuitive and I've come across it before. I believe it's considered a feature rather than a bug though and is necessary for plotly express to work: https://github.com/plotly/plotly.py/issues/3720.

antonymilne avatar Nov 12 '25 13:11 antonymilne