Version 0.13.2

ButtonLink

A button-styled link with primary, secondary, and tertiary variants.

Usage#

import { ButtonLink } from '@backstage/ui';

<ButtonLink href="https://ui.backstage.io">Button</ButtonLink>

API reference#

PropTypeDefaultDescription
variant
primarysecondarytertiary
primaryVisual style. Use primary for main actions, secondary for alternatives, tertiary for low-emphasis.
size
smallmedium
smallLink size. Use small for inline contexts, medium for standalone.
iconStart
ReactElement
-Icon displayed before the link text.
iconEnd
ReactElement
-Icon displayed after the link text.
isDisabled
boolean
falsePrevents interaction and applies disabled styling.
href
string
-URL the link navigates to.
target
_self_blank_parent_top
-Where to open the linked URL. Use _blank for external links.
noTrack
boolean
-Suppresses the automatic analytics click event, e.g. if you already have custom tracking.
onSurface
0123dangerwarningsuccessauto
-Surface context for correct color contrast.
children
ReactNode
-
className
string
-Additional CSS class name for custom styling.
style
CSSProperties
-Inline CSS styles object.

Inherits all React Aria Link props.

Examples#

Sizes#

<Flex align="center">
  <ButtonLink size="small" href="https://ui.backstage.io" target="_blank">
    Small
  </ButtonLink>
  <ButtonLink size="medium" href="https://ui.backstage.io" target="_blank">
    Medium
  </ButtonLink>
</Flex>

With Icons#

<Flex align="center">
  <ButtonLink iconStart={<RiCloudLine />} href="https://ui.backstage.io" target="_blank">
    Button
  </ButtonLink>
  <ButtonLink iconEnd={<RiArrowRightSLine />} href="https://ui.backstage.io" target="_blank">
    Button
  </ButtonLink>
  <ButtonLink
    iconStart={<RiCloudLine />}
    iconEnd={<RiArrowRightSLine />}
    href="https://ui.backstage.io"
    target="_blank"
  >
    Button
  </ButtonLink>
</Flex>

Disabled#

PrimarySecondaryTertiary
<Flex gap="4">
  <ButtonLink variant="primary" isDisabled href="https://ui.backstage.io" target="_blank">
    Primary
  </ButtonLink>
  <ButtonLink variant="secondary" isDisabled href="https://ui.backstage.io" target="_blank">
    Secondary
  </ButtonLink>
  <ButtonLink variant="tertiary" isDisabled href="https://ui.backstage.io" target="_blank">
    Tertiary
  </ButtonLink>
</Flex>

Theming#

Our theming system is based on a mix between CSS classes, CSS variables and data attributes. If you want to customise this component, you can use one of these class names below.

  • bui-ButtonLink
  • bui-ButtonLinkContent

Changelog#

Version 0.13.0#

Breaking Changes

  • Breaking Centralized client-side routing in BUIProvider. Components like Link, ButtonLink, Tabs, Menu, TagGroup, and Table now require a BUIProvider rendered inside a React Router context for client-side navigation to work. #33267

    Migration Guide:

    This change requires updating @backstage/plugin-app and @backstage/core-app-api alongside @backstage/ui. If you only upgrade @backstage/ui, BUI components will fall back to full-page navigation.

    If you cannot upgrade all packages together, or if you have a custom app shell, add a BUIProvider inside your Router:

    + import { BUIProvider } from '@backstage/ui';
    
      <BrowserRouter>
    +   <BUIProvider>
          <AppContent />
    +   </BUIProvider>
      </BrowserRouter>
    

Changes

  • Added analytics capabilities to the component library. Components with navigation behavior (Link, ButtonLink, Tab, MenuItem, Tag, Row) now fire analytics events on click when a BUIProvider is present.

    New exports: BUIProvider, useAnalytics, getNodeText, and associated types (AnalyticsTracker, UseAnalyticsFn, BUIProviderProps, AnalyticsEventAttributes).

    Components with analytics support now accept a noTrack prop to suppress event firing. #33150

  • Fixed focus ring styles to use React Aria's [data-focus-visible] data attribute instead of the native CSS :focus-visible pseudo-class. This ensures keyboard focus rings render reliably when focus is managed programmatically by React Aria (e.g. inside a GridList, Menu, or Select). #33358

  • Fixed handling of the style prop on Button, ButtonIcon, and ButtonLink so that it is now correctly forwarded to the underlying element instead of being silently dropped. #33095

Version 0.12.0#

Breaking Changes

  • Breaking Replaced Surface / onSurface system with new provider/consumer background system

    The old Surface type ('0''3', 'auto') and its associated props (surface, onSurface) have been replaced by a provider/consumer bg architecture.

    Types:

    • ContainerBg'neutral-1' | 'neutral-2' | 'neutral-3' | 'danger' | 'warning' | 'success'
    • ProviderBgContainerBg | 'neutral-auto'

    Consumer components (e.g. Button) inherit the parent's bg via data-on-bg, and CSS handles the visual step-up. See "Neutral level capping" below for details on how levels are bounded.

    Hooks:

    • useBgProvider(bg?) — for provider components. Returns { bg: undefined } when no bg is given (transparent). Supports 'neutral-auto' to auto-increment from the parent context.
    • useBgConsumer() — for consumer components. Returns the parent container's bg unchanged.

    Component roles:

    • Provider-only (Box, Flex, Grid): set data-bg, wrap children in BgProvider. Transparent by default — they do not auto-increment; pass bg="neutral-auto" explicitly if you want automatic neutral stepping.
    • Consumer-only (Button, ButtonIcon, ButtonLink): set data-on-bg, inherit the parent container's bg unchanged.
    • Provider + Consumer (Card): sets both data-bg and data-on-bg, wraps children. Card passes bg="neutral-auto" to its inner Box, so it auto-increments from the parent context.

    Neutral level capping:

    Provider components cap at neutral-3. There is no neutral-4 prop value. The neutral-4 level exists only in consumer component CSS — for example, a Button sitting on a neutral-3 surface uses neutral-4 tokens internally via data-on-bg. #32711

    Migration Guide:

    Rename the surface prop to bg on provider components and update values:

    - <Box surface="1">
    + <Box bg="neutral-1">
    
    - <Card surface="2">
    + <Card bg="neutral-2">
    
    - <Flex surface="0">
    + <Flex bg="neutral-1">
    
    - <Grid.Root surface="1">
    + <Grid.Root bg="neutral-1">
    

    Remove onSurface from consumer components — they now always inherit from the parent container:

    - <Button onSurface="1" variant="secondary">
    + <Button variant="secondary">
    
    - <ButtonIcon onSurface="2" variant="secondary" />
    + <ButtonIcon variant="secondary" />
    
    - <ToggleButton onSurface="1">
    + <ToggleButton>
    

    Update type imports:

    - import type { Surface, LeafSurfaceProps, ContainerSurfaceProps } from '@backstage/ui';
    + import type { ContainerBg, ProviderBg } from '@backstage/ui';
    

    Replace hook usage in custom components:

    - import { useSurface, SurfaceProvider } from '@backstage/ui';
    + import { useBgProvider, useBgConsumer, BgProvider } from '@backstage/ui';
    
    - const { surface } = useSurface({ surface: props.surface });
    + const { bg } = useBgProvider(props.bg);
    
    - const { surface } = useSurface({ onSurface: props.onSurface });
    + const { bg } = useBgConsumer();
    

    Update CSS selectors targeting surface data attributes:

    - [data-surface='1'] { ... }
    + [data-bg='neutral-1'] { ... }
    
    - [data-on-surface='1'] { ... }
    + [data-on-bg='neutral-1'] { ... }
    

    Note: Provider components use data-bg (values: neutral-1 through neutral-3, plus intent values). Consumer components use data-on-bg, which reflects the parent container's bg directly. The neutral-4 level never appears as a prop or data-bg value — it is used only in consumer CSS.

  • Breaking Changed CSS selectors for ButtonIcon and ButtonLink components. Custom styles targeting .bui-Button to style these components must be updated to use .bui-ButtonIcon or .bui-ButtonLink respectively.

    -/* This no longer styles ButtonIcon or ButtonLink */
    -.bui-Button[data-variant="primary"] { ... }
    +/* Use component-specific selectors */
    +.bui-ButtonIcon[data-variant="primary"] { ... }
    +.bui-ButtonLink[data-variant="primary"] { ... }
    ``` [#32308](https://github.com/backstage/backstage/pull/32308)
    

Changes

  • Fixed components to not require a Router context when rendering without internal links. #32373