Skip to content

Button: external_link leaks to the DOM as an attribute when the button renders in non-link mode (React non-boolean attribute warning) #1166

Description

@chilango74
  • 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.:

  1. 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
  2. href is set but the button is disabledhref={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
    ...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions