Repository navigation
Bug: make_subplots() doesn't use shared x-axes for traces on different subplots, even when shared_xaxes is set to True #5427
Description
Activity
Assigning to @emilykl to take a look and triage.
Thanks for the ticket @videni!
I'm able to reproduce the issue. Shared spikelines across multiple subplots should be supported, and it seems like the root cause is a bug in
make_subplots()which is assigning different x-axes to the two traces even though the subplots were created with the settingshared_xaxes=True.As a workaround, I'm able to get the shared spikeline behavior by manually setting both data traces to use the same
xaxis by adding this line after theadd_trace()calls:fig.update_traces(xaxis='x')
I would consider this a bug; I'll update the issue description to match and we'll do our best to take a look when we have the chance.
- changed the title
[-]Spike lines don't show across all subplots simultaneously with shared x-axis[/-][+]`make_subplots()` doesn't use shared x-axes even when `shared_xaxes` is set to True[/+]on Nov 24, 2025 Small example demonstrating the shared x-axis behavior with
make_subplots(shared_xaxes=True). The code snippet below produces the following plot JSON, where the two subplots have separate x-axes (xandx2), but use theaxis.matchedparameter to share the same range. Traces added to the figure then automatically use the x-axis of whichever subplot they are added to.This means that the x-axes of the two plots remain synced when zooming and panning, BUT it means that features such as cross-subplot unified hover and spikelines don't actually work across subplots.
I think it's probably OK to have different x-axes for the individual subplots (because this allows us to do things like hide the axis tick labels on the top subplot), but I think that traces added via
add_trace()should then always refer to the main x-axis (x), as this will allow the cross-subplot hover behavior to work as expected.Note: I suspect the same behavior also applies to the y-axis but I haven't tested it.
import plotly.graph_objects as go from plotly.subplots import make_subplots # Create two vertically-stacked subplots with shared x-axis fig = make_subplots(rows=2, cols=1, shared_xaxes=True) # Add one trace to each subplot fig.add_trace(go.Scatter(x=[1,2,3], y=[1,2,3]), row=1, col=1) fig.add_trace(go.Scatter(x=[1,2,3], y=[1,3,2]), row=2, col=1) print(fig.to_json())
Plot JSON:
Details
{ "data": [ { "x": [1, 2, 3], "y": [1, 2, 3], "type": "scatter", "xaxis": "x", "yaxis": "y" }, { "x": [1, 2, 3], "y": [1, 3, 2], "type": "scatter", "xaxis": "x2", "yaxis": "y2" } ], "layout": { "template": {...}, "xaxis": { "anchor": "y", "domain": [0.0, 1.0], "matches": "x2", "showticklabels": false }, "yaxis": { "anchor": "x", "domain": [0.575, 1.0] }, "xaxis2": { "anchor": "y2", "domain": [0.0, 1.0] }, "yaxis2": { "anchor": "x2", "domain": [0.0, 0.425] } } }- changed the title
[-]`make_subplots()` doesn't use shared x-axes even when `shared_xaxes` is set to True[/-][+]Bug: `make_subplots()` doesn't use shared x-axes for traces on different subplots, even when `shared_xaxes` is set to True[/+]on Nov 24, 2025 Hello, do we have any updates about this bug?
@AnneEstoppey Realistically we're not likely to address this in the near future, but a workaround is to add
fig.update_traces(xaxis='x')
after adding traces to a plot.
If you are interested in working on a PR for this issue we'd also be happy to assist.
Hi @emilykl
Can I try taking this bug?
@richmanpoorman Absolutely, go ahead.
As I mentioned in my comment above, I think the solution is to modify the
add_trace()method such that traces added viaadd_trace()to a plot with matched x-axes reference the main x-axis (x), rather than the x-axis of the plot they are added to. Likewise for matched y-axes.However I haven't yet tried implementing this myself so not sure if there are additional complications.
Hi,
I just wanted to follow up because I think that I found the bug, but there needs to be a note on the workaround in the mean time. Thefig.update_traces(xaxis='x')actually causes a bug where the scale at the bottom disappears.This is because it should be
fig.update_traces(xaxis='x2')to make the scale come back, as the code internally makes each of the figures a unique value, and only shows the scale on the bottom/last one (so if would bexnfor n=number of figures)@emilykl Hi, I tried to use that method, but it seems that there are tests that explicitly expect the plots to be the x-axis they are added to; should I change these tests to match the expectation you have listed above, or should I revert the changes I have? (Note that the change has to be made in make_subplots, as that is where the x-axis references are defined)
In particular, I am failing a test in tests/test_optional/test_figure_factory/test_figure_factory.py
- marked Shared x-axis does not work when using grid layout #2705 as a duplicate of this issue
on Aug 31, 2026 @richmanpoorman are you still planning to post the proposal emilykl asked for when she closed #5581? If not, I'd like to take this over and write up an approach here first.
Hi, I can summarize my findings here.
Basically, I think the bug actually stems from a deeper issue in the JS repository with how things are drawn, where the spike line only looks at the current plot and doesn't consider that there are other plots that may match with axis (aka the line getting drawn checks by plot rather than by axis). Since each plot has its own unique set of x and y axis labels (ie "x", "x2", ...) it doesn't draw the spiked line even if the plot says its supposed to match. If you were to fix this, it would going to where the spiked line drawing is in the code, and adding a check to also draw the spikeline on plots that match (aka if drawing a y-axis spikeline, you need to also check the other plots on that y-axis to see if they match that y-axis and vice versa).
However, we can have a work around where reassign the axis to have the same label as the one you want to match instead. However, the problem with the work around that was listed above was that you need to choose the right axis to assign all of them to, depending on where the labels are. You need to do this, otherwise the tick values will disappear or are on the wrong plot (not at the bottom or top) as the ticks should have the option to be "turned off" for the intermediate graphs (ie if they share the x-axis, only the bottom or top [if it is above] needs to have the ticks shown, you can hide it for the others). You also need to make sure to consider secondary_y labels too, as this method will cause a bug where the secondary_y axis will not be "synced up", as the secondary_y technically creates a distinct y_axis alongside the current y_axis. In addition, there can be an error with the "all" mode if you don't do it properly, where all the graphs can be put into one subplot.
In retrospect, there is a much cleaner way to solve this problem, as I realize I could just make the changes at the end, rather than trying to make the changes during construction, but the edge cases are why the code change was a bit messy. I might go back and clean it up and refactor what I had done.
There is also conflict with some tests which expect the axes to be unique between subplots, but in this case, we are purposefully making them the same.
Please let me know if there are any questions or concerns, and I will try to clarify the best that I can
Reacted by Cameron DeCoster
Description
When using
make_subplots()withshared_xaxes=True, spike lines only appear on the subplot where the cursor is hovering, rather than showing across all subplots simultaneously.Expected Behavior
When hovering over any subplot, a vertical spike line should appear at the same x-position across all subplots, similar to the behavior in TradingView or other financial charting tools.
Current Behavior
The spike line only appears on the subplot currently under the cursor. When moving to a different subplot, the spike line moves with the cursor instead of showing on all subplots.
Code Example
What I've Tried
hovermode='x unified'- Only unifies hover labels, not spike linesfig.update_traces(xaxis='x')- Shows spikes across all subplots but breaks subplot layout (all data gets squeezed into first subplot's x-axis range)spikemode,spikesnap, andmatches='x'- No effectUse Case
This feature is critical for financial/trading dashboards where users need to compare multiple indicators (price, volume, RSI, MACD, etc.) at the same point in time across multiple subplots.