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
ResizeObserver callback on the resizing/disappearing container.
- The callback runs query selectors inside the target for
.shiny-bound-output and 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
Component
UI (ui.*)
Severity
P2 - Medium (workaround exists)
Shiny Version
1.6.1+
Python Version
3.12
Minimal Reproducible Example
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:
shinyOutputBinding).ResizeObservercallback on the resizing/disappearing container..shiny-bound-outputand finds the detached elements.$(el).data("shinyOutputBinding")returns undefined.Proposed Solution
It seems like the underlying issue is actually in bslib.
A defensive check or fallback should be added in
_shinyResizeObserver.tsto safely ignore elements whose bindings have already been torn down or are not yet set:Or using a fallback:
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)
Environment