An icon-only button with primary, secondary, and tertiary variants.
import { ButtonIcon } from '@backstage/ui';
import { RiCloseLine } from '@remixicon/react';
<ButtonIcon icon={<RiCloseLine />} aria-label="Close" />| Prop | Type | Default | Description |
|---|---|---|---|
| variant | primarysecondarytertiary | primary | Visual style. Use primary for main actions, secondary for alternatives, tertiary for low-emphasis. |
| size | smallmedium | small | Button size. Use small for toolbars, medium for standalone actions. |
| icon | ReactElement | - | Icon element to display. Required for accessibility via aria-label. |
| isDisabled | boolean | false | Prevents interaction and applies disabled styling. |
| loading | boolean | false | Shows a spinner and disables the button. |
| type | buttonsubmitreset | button | HTML button type attribute. |
| onSurface | 0123dangerwarningsuccessauto | - | Surface context for correct color contrast. |
| className | string | - | Additional CSS class name for custom styling. |
| style | CSSProperties | - | Inline CSS styles object. |
Inherits all React Aria Button props.
<Flex align="center" gap="2">
<ButtonIcon icon={<RiCloudLine />} variant="primary" aria-label="Cloud" />
<ButtonIcon icon={<RiCloudLine />} variant="secondary" aria-label="Cloud" />
<ButtonIcon icon={<RiCloudLine />} variant="tertiary" aria-label="Cloud" />
</Flex><Flex direction="row" gap="2">
<ButtonIcon isDisabled icon={<RiCloudLine />} variant="primary" aria-label="Cloud" />
<ButtonIcon isDisabled icon={<RiCloudLine />} variant="secondary" aria-label="Cloud" />
<ButtonIcon isDisabled icon={<RiCloudLine />} variant="tertiary" aria-label="Cloud" />
</Flex>Shows a spinner during async operations.
<ButtonIcon icon={<RiCloudLine />} variant="primary" loading aria-label="Cloud" />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-ButtonIconbui-ButtonIconContentbui-ButtonIconSpinnerFixed 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
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'ProviderBg — ContainerBg | '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:
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.data-on-bg, inherit the parent container's bg unchanged.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)
ButtonIcon incorrectly applying className to inner elements instead of only the root element. #31900loading prop to Button and ButtonIcon components for displaying spinner during async operations. #31681