For AI agents: the complete documentation index is available at /ooui-react/en/llms.txt, the full documentation bundle is available at /ooui-react/en/llms-full.txt, and this page is available as Markdown at /ooui-react/en/components/button/index.md.
  • English
  • Button

    Source | Original component

    The basic button component, and also the button base for ToggleButton, PopupButton, ButtonMenuSelectWidget and other components.

    Basic usage

    Click meClicked 0 times
    import { useState } from "react";
    import { Button } from "ooui-react";
    
    function App() {
      const [count, setCount] = useState(0);
    
      return (
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <Button onClick={() => setCount((n) => n + 1)}>Click me</Button>
          <span>Clicked {count} times</span>
        </div>
      );
    }
    
    export default App;

    Icons and indicators

    icon takes an icon name (see Icon), indicator takes one of the four indicators (see Indicator). They render on opposite ends of the label.

    When the label is icon-only, turn on invisibleLabel: the label is visually hidden but kept as the accessible name, and title falls back to the label text when not given explicitly.

    import { Button } from "ooui-react";
    
    function App() {
      return (
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <Button icon="check">Done</Button>
          <Button icon="trash" flags="destructive">
            Delete
          </Button>
          <Button indicator="down">Expand</Button>
          <Button icon="search" invisibleLabel>
            Search
          </Button>
        </div>
      );
    }
    
    export default App;

    Flags and variants

    flags affect color and icon tinting, framed={false} switches to the unframed form, and active marks a selected/activated state.

    import { Button } from "ooui-react";
    
    function App() {
      return (
        <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
          <Button>Default</Button>
          <Button flags="progressive">Continue</Button>
          <Button flags={["primary", "progressive"]}>Save</Button>
          <Button flags="destructive">Delete</Button>
          <Button framed={false} flags="destructive">
            Unframed delete
          </Button>
          <Button active>Active</Button>
        </div>
      );
    }
    
    export default App;

    A framed button inverts as a whole when primary, active, or disabled, and the icon and indicator invert with it; otherwise it is tinted by the color flag in flags. The available color flags are progressive, destructive, invert, error, warning and success.

    Given href, the inner <a> becomes a navigable link and target chooses where it opens. rel defaults to nofollow; when using target="_blank" you should add noopener yourself.

    import { Button } from "ooui-react";
    
    function App() {
      return (
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <Button href="https://www.mediawiki.org">MediaWiki</Button>
          <Button href="https://www.mediawiki.org" target="_blank" rel={["nofollow", "noopener"]}>
            Open in a new tab
          </Button>
        </div>
      );
    }
    
    export default App;
    Security note

    This component does not sanitize href (it bypasses the original OO.ui.isSafeUrl), and React does not block dangerous schemes such as javascript: at runtime either. Links of untrusted origin (e.g. concatenated from user input) must have their protocol validated by you first, otherwise this is an XSS; when you want component-level sanitizing, use the exported sanitizeUrl.

    Disabled

    disabled outputs oo-ui-widget-disabled and aria-disabled (not the native disabled); the button then ignores clicks, its tabindex becomes -1, and href no longer takes effect. Inside a ButtonGroup, the group-level disabled state propagates to the buttons in it.

    import { Button } from "ooui-react";
    
    function App() {
      return (
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <Button disabled>Disabled</Button>
          <Button icon="check" disabled>
            Disabled (with icon)
          </Button>
        </div>
      );
    }
    
    export default App;

    Focus and keyboard

    • The focusable element is the inner <a>; the root <span> is not focusable. To focus the button manually, use anchorRef (the component ref points to the outer <span>).
    • Enter / Space trigger onClick; onKeyPress prevents Space from scrolling the page whenever a click handler is present.
    • The pressed state (oo-ui-buttonElement-pressed) is maintained by the component in JS: CSS has no keyboard-press pseudo-class, so keyboard press and release are handled by explicit event listeners. The pressed prop is OR-ed with it, for cases like "keep the button pressed while its associated menu is open".
    • title and accessKey land on the inner <a>; when title is not given and the label is hidden it falls back to the label text, and when accessKey is present a key hint is appended to title (the chord text can be localized via OOUIProvider).

    API

    PropDescriptionTypeDefault
    childrenButton labelReactNode—
    iconLeading icon namestring—
    indicatorTrailing indicator'up' | 'down' | 'clear' | 'required'—
    flagsExtra flags (color and button-specific form)ButtonFlag | ButtonFlag[][]
    framedWhether it has a borderbooleantrue
    activeWhether it is in the active statebooleanfalse
    invisibleLabelLabel visually hidden (kept as accessible name)booleanfalse
    disabledWhether disabled (incl. the owning ButtonGroup's group disable)booleanfalse
    hrefLink target (written on the inner <a>)string—
    targetWhere the link opensstring—
    relThe inner <a>'s rel (an array is joined by spaces)string | string[]['nofollow']
    titleTooltip text for the inner <a>string—
    accessKeyAccess keystring—
    tabIndexTab order; null means the attribute is omittednumber | null0
    pressedControlled pressed state (OR-ed with the internal press flow)boolean—
    onClickClick handler (also fired by Enter / Space)(ev: ButtonClickEvent) => void—
    anchorRefRef to the inner <a> elementRef<HTMLAnchorElement>—
    anchorPropsExtra props for the inner <a>; role, tabIndex, aria-disabled, etc. are taken over by the component and take priorityHTMLAttributes<HTMLAnchorElement>—
    iconProps / indicatorPropsExtra props for the icon / indicator elementobject—
    ...restNative span props (className, id, data-*, aria-*, etc.) passed straight to the rootHTMLAttributes<HTMLSpanElement>—

    ButtonFlag is 'progressive' | 'destructive' | 'invert' | 'error' | 'warning' | 'success' | 'primary' | 'safe' | 'back' | 'close'; the first six decide tinting, the last four are button-specific form flags.