Repository navigation
Description
Component
UI (ui.*)
Severity
P2 - Medium (workaround exists)
Shiny Version
1.6.1+
Python Version
3.12
Minimal Reproducible Example
from shiny import App, render, ui
import matplotlib.pyplot as plt
app_ui = ui.page_fluid(
ui.input_action_button("open_modal", "Open Modal with Plot"),
)
def server(input, output, session):
@render.plot
def my_plot():
fig, ax = plt.subplots()
ax.plot([1, 2], [3, 4])
return fig
@input.open_modal
def _():
ui.modal_show(
ui.modal(
ui.card(
ui.card_header("Card inside Modal"),
ui.output_plot("my_plot"),
),
title="Reproduction Modal",
easy_close=True
)
)
app = App(app_ui, server)Behavior
When a container that is registered with the ShinyResizeObserver (such as a card, sidebar, or a layout component inside a modal) is removed from the DOM, the observer does not automatically stop watching it.
During the cleanup/removal transition:
- The DOM elements are detached/unbound, which clears their associated jQuery data (
shinyOutputBinding). - The browser triggers a final
ResizeObservercallback on the resizing/disappearing container. - The callback runs query selectors inside the target for
.shiny-bound-outputand finds the detached elements. - Because the elements are detached/unbound, calling
$(el).data("shinyOutputBinding")returns undefined. - Destructuring binding and onResize from undefined throws an uncaught JavaScript error, halting the client-side execution.
Proposed Solution
It seems like the underlying issue is actually in bslib.
A defensive check or fallback should be added in _shinyResizeObserver.ts to safely ignore elements whose bindings have already been torn down or are not yet set:
const outputData = $(el).data("shinyOutputBinding");
if (!outputData) return;
const { binding, onResize } = outputData;
if (!binding || !binding.resize) return;Or using a fallback:
const { binding, onResize } = $(el).data("shinyOutputBinding") || {};
if (!binding || !binding.resize) return;Disclaimer: This Bug Report was prepared with the help of Antigravity and manually verified.
I greatly appreciate all the hard work the maintainers have poured into the shiny ecosystem and would not like to add AI slop here 😅
Error Messages (if any)
Uncaught TypeError: Cannot destructure property 'binding' of '$(...).data(...)' as it is undefined.
at _shinyResizeObserver.ts:81:21
at NodeList.forEach (<anonymous>)
at ResizeObserver.<anonymous> (_shinyResizeObserver.ts:78:12)Environment
Python package: shiny (version 1.6.0 / 1.6.1+)
Underlying UI package: bslib (frontend assets compiled from rstudio/bslib version 0.10.0 / 0.11.0+)
OS: Win10
Browser: Edge 149.0.4022.69
Activity
slupczynskim commentedon Jun 13, 2026
For anyone also experiencing this issue before it's fixed, add this to the javascript in your frontend.
For me, the bug triggers when opening/closing multiple modals with interactive contents inside:
cpsievert commentedon Jun 15, 2026
When I run your example, this is what I see (btw,
input.open_modelis not intended to be used as a decorator):Also the latest shiny release (v1.6.3) includes a resize observer fix that might be related to this -- can you reproduce with the latest release?
ebarkhordar commentedon Aug 14, 2026
On the version question: the resize fix in 1.6.3 changed
IntersectionObserverfor nested outputs, not this path._shinyResizeObserver.ts:81in bslib main is still the unguardedconst { binding, onResize } = $(el).data("shinyOutputBinding"), and no guarded variant appears in any bundle shipped with 1.6.0 through 1.7.0, so upgrading will not move it.Rewriting the example with
@reactive.effectplus@reactive.event(input.open_modal), I could not trigger the error: headless Chromium, shiny 1.6.0 and 1.6.1, six plot outputs per modal, 40 open/close cycles over four close paths. That neither confirms nor rules out the report.