- dash version:
4.2.0
- dash-bootstrap-components version:
2.0.4 (the relevant code is unchanged on current main)
- components affected by bug:
dbc.Button (verified; other components destructure external_link themselves)
What is happening?
When a dbc.Button carries external_link=True but renders in non-link mode, the external_link prop is spread onto the underlying DOM <button> element, and React logs a dev-console warning:
Non-link mode happens whenever useLink = href && !disabled is false, i.e.:
href is not set — e.g. the layout declares the button and a callback fills href later (link-button whose URL is computed from form state), or
href is set but the button is disabled — href={disabled ? undefined : href} strips the href, so a disabled link-button falls back to <button> and leaks too.
In src/components/button/Button.js external_link is not destructured in the function signature, so it travels inside ...otherProps and is spread onto <RBButton as='button' ...>. (In link mode it ends up consumed by the private Link component, which does destructure it — no leak there.)
React drops the attribute (it never appears in the DOM), so the impact is dev-console noise — but it fires on every page load for this common pattern, and external_link is a documented dbc prop being handed to the DOM rather than handled by the component.
What should be happening?
external_link should never reach the DOM element. In non-link mode it should simply be dropped (it only has meaning for links), the same way target is special-cased via linkTarget. E.g. destructure external_link in Button and forward it explicitly only when useLink:
function Button({ ..., external_link, ...otherProps }) {
...
if (useLink) {
otherProps['external_link'] = external_link;
otherProps['linkTarget'] = target;
}
Note external_link=True can't just be omitted by the app in this pattern: with a relative href and target="_blank", the private Link strips target and does same-tab SPA navigation unless external_link is true — so the prop has to be present in the layout even before href arrives.
Code
import dash
import dash_bootstrap_components as dbc
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Button(
"Open report in new tab",
id="report-link",
external_link=True,
target="_blank",
# href intentionally absent: a callback computes it from form state later.
# Same warning if instead: href="/report", disabled=True
)
if __name__ == "__main__":
app.run(debug=True)
Open the browser dev console on page load.
Error messages
Warning: Received `true` for a non-boolean attribute `external_link`.
If you want to write it to the DOM, pass a string instead: external_link="true" or external_link={value.toString()}.
at button
at https://.../dash_bootstrap_components.v2_0_4m1779801954.min.js:2:52987
...
4.2.02.0.4(the relevant code is unchanged on currentmain)dbc.Button(verified; other components destructureexternal_linkthemselves)What is happening?
When a
dbc.Buttoncarriesexternal_link=Truebut renders in non-link mode, theexternal_linkprop is spread onto the underlying DOM<button>element, and React logs a dev-console warning:Non-link mode happens whenever
useLink = href && !disabledis false, i.e.:hrefis not set — e.g. the layout declares the button and a callback fillshreflater (link-button whose URL is computed from form state), orhrefis set but the button isdisabled—href={disabled ? undefined : href}strips the href, so a disabled link-button falls back to<button>and leaks too.In
src/components/button/Button.jsexternal_linkis not destructured in the function signature, so it travels inside...otherPropsand is spread onto<RBButton as='button' ...>. (In link mode it ends up consumed by the privateLinkcomponent, which does destructure it — no leak there.)React drops the attribute (it never appears in the DOM), so the impact is dev-console noise — but it fires on every page load for this common pattern, and
external_linkis a documented dbc prop being handed to the DOM rather than handled by the component.What should be happening?
external_linkshould never reach the DOM element. In non-link mode it should simply be dropped (it only has meaning for links), the same waytargetis special-cased vialinkTarget. E.g. destructureexternal_linkinButtonand forward it explicitly only whenuseLink:Note
external_link=Truecan't just be omitted by the app in this pattern: with a relativehrefandtarget="_blank", the privateLinkstripstargetand does same-tab SPA navigation unlessexternal_linkis true — so the prop has to be present in the layout even beforehrefarrives.Code
Open the browser dev console on page load.
Error messages