New features
-
New
startApp()runs a Shiny app in non-blocking mode, returning a
ShinyAppHandleobject withstop(),status(),url(), andresult()
methods. When a new app is started, any previously running non-blocking app
is automatically stopped. (#4349) -
session$destroy()andsession$onDestroy()are now available on
module session proxies to clean up "dangling reactivity" when dynamic
module UI is removed. Callingsession$destroy()invokes all
registeredonDestroy()callbacks for that scope and its descendants,
tearing down reactive values, expressions, and observers. A parent can
also destroy a child module scope by id withsession$destroy(id), so it
can tear down a module using the same id it used to insert the UI (#4372). -
downloadButton()anddownloadLink()gain a newenabledparameter. The
default value,"auto", automatically enables the button/link when the
download is ready. To opt-into manual state management (e.g.,
shinyjs::enable()), setenabledtoFALSE(orTRUE). (#4119)
Improvements
-
Output resize/visibility detection now uses native browser observers
(ResizeObserver,IntersectionObserver) instead of relying on jQuery
shown/hiddenevents andwindow.resize. This makes Shiny's client-side
output-info pipeline (image/plot sizing, hidden-state tracking, theme
reporting) work automatically in any layout — including CSS-only show/hide,
third-party tab components, and non-Bootstrap frameworks — without requiring
custom event hooks. This also introduces ashiny:themechangeevent
for code that needs to trigger theme clientdata refreshes after changing
surrounding visual theme context. (#3682) -
conditionalPanel()no longer briefly flashes its contents on app start
when the condition is initiallyFALSE. (#3505) -
Updated default HTTP headers for better security. Shiny now sends
X-Content-Type-Options: nosniffinstead of the legacyX-UA-Compatible
header. This removes outdated Internet Explorer–specific behavior and adds a
modern safeguard that prevents browsers from misinterpreting file types.
(#4385) -
Removed
InputBinding.dispose()from the JavaScriptInputBindingclass.
This method was never called by Shiny's runtime, so any overrides were dead
code. Useunsubscribe()for cleanup logic instead. (#4375)
Bug fixes
-
Loading shiny no longer creates
.Random.seedin the global environment as a
side effect. (#4382) -
need()now gives a clearer error when called without either amessageor
labelargument, instead of the cryptic "argument "label" is missing, with
no default". (thanks @chasemc and @sundrelingam, #2509) -
Clarified
varSelectInput()documentation to explain that the input
returns a symbol for use with tidy evaluation, and fixed a grammatical
typo. (#2334)