Version 0.13.2

Changelog

Version 0.13.0#

Breaking Changes#

  • Box Flex Grid Card Accordion Popover Tooltip Dialog Menu Breaking Simplified the neutral background prop API for container components. The explicit neutral-1, neutral-2, neutral-3, and neutral-auto values have been removed from ProviderBg. They are replaced by a single 'neutral' value that always auto-increments from the parent context, making it impossible to skip or pin to an explicit neutral level. #33002

    Migration Guide:

    Replace any explicit bg="neutral-1", bg="neutral-2", bg="neutral-3", or bg="neutral-auto" props with bg="neutral". To achieve a specific neutral level in stories or tests, use nested containers — each additional bg="neutral" wrapper increments by one level.

    // Before
    <Box bg="neutral-2">...</Box>
    
    // After
    <Box bg="neutral">
      <Box bg="neutral">...</Box>
    </Box>
    
  • Popover Tooltip Menu Dialog Breaking Removed --bui-bg-popover CSS token. Popover, Tooltip, Menu, and Dialog now use --bui-bg-app for their outer shell and Box bg="neutral-1" for content areas, providing better theme consistency and eliminating a redundant token. #32979

    Migration Guide:

    Replace any usage of --bui-bg-popover with --bui-bg-neutral-1 (for content surfaces) or --bui-bg-app (for outer shells):

    - background: var(--bui-bg-popover);
    + background: var(--bui-bg-neutral-1);
    
  • Searchfield Breaking Data attributes rendered by components are now always lowercase. This affects CSS selectors targeting camelCase data attributes. #33050

    Migration Guide:

    Update any custom CSS selectors that target camelCase data attributes to use lowercase instead:

    - [data-startCollapsed='true'] { ... }
    + [data-startcollapsed='true'] { ... }
    
  • Breaking Removed deprecated types ComponentDefinition, ClassNamesMap, DataAttributeValues, and DataAttributesMap from the public API. These were internal styling infrastructure types that have been replaced by the defineComponent system. #33050

    Migration Guide:

    Remove any direct usage of these types. Component definitions now use defineComponent() and their shapes are not part of the public API contract.

    - import type { ComponentDefinition, ClassNamesMap } from '@backstage/ui';
    

    If you were reading definition.dataAttributes, use definition.propDefs instead — props with dataAttribute: true in propDefs are the equivalent.

  • Link Button Link Tabs Menu Tag Group Table 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>
    
  • Header Breaking Renamed internal CSS classes to match the Header component name.

    Migration: If you are targeting these classes directly in your styles, update the following:

    • bui-HeaderPagebui-Header
    • bui-HeaderPageContentbui-HeaderContent
    • bui-HeaderPageBreadcrumbsbui-HeaderBreadcrumbs
    • bui-HeaderPageTabsWrapperbui-HeaderTabsWrapper
    • bui-HeaderPageControlsbui-HeaderControls #33354
  • Checkbox Breaking Removed redundant selected and indeterminate props from the Checkbox component. Use the isSelected and isIndeterminate props instead, which are the standard React Aria props and already handle both the checkbox behaviour and the corresponding CSS data attributes. #33323

    Migration Guide:

    Replace any usage of the selected and indeterminate props on Checkbox with the isSelected and isIndeterminate props. Note that the checked state and related CSS data attributes (such as data-selected and data-indeterminate) are now driven by React Aria, so any custom logic that previously inspected or set these via the old props should instead rely on the React Aria-managed state and attributes exposed through the new props.

Changes#

  • List List Row Added List and ListRow components. These provide a standalone, accessible list of interactive rows built on top of React Aria's GridList and GridListItem primitives. Rows support icons, descriptions, actions, menus, and single or multiple selection modes. #33358

  • Accordion Made Accordion a bg provider so nested components like Button auto-increment their background level. Updated useDefinition to resolve bg propDef defaults for provider components. #32935

  • Card Added interactive support to the Card component. Pass onPress to make the entire card surface pressable, or href to make it navigate to a URL. A transparent overlay handles the interaction while nested buttons and links remain independently clickable. #33116

  • Link Button Link Tab Menu Item Tag Row 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

  • Table Root Added a loading prop and data-loading data attribute to TableRoot, allowing consumers to distinguish between stale data and initial loading states. Both stale and loading set aria-busy on the table. #33322

  • Table Improved the Table component loading state to show a skeleton UI with visible headers instead of plain "Loading..." text. The table now renders its full structure during loading, with animated skeleton rows in place of data. The loading state includes proper accessibility support with aria-busy on the table and screen reader announcements. #33322

  • Fixed neutral-1 hover & pressed state in light mode. #33078

  • Avatar Checkbox Container Dialog Field Error Field Label Flex Full Page Grid Header Page Link Menu Password Field Plugin Header Popover Radio Group Searchfield Select Skeleton Switch Table Table Pagination Tabs Tag Group Text Textfield Toggle Button Toggle Button Group Tooltip Visually Hidden Migrated all components from useStyles to useDefinition hook. Exported OwnProps types for each component, enabling better type composition for consumers. #33050

  • Card Fixed interactive cards so that CardBody can scroll when the card has a constrained height. Previously, the overlay element blocked scroll events. #33151

  • Container Removed the transition on Container padding to prevent an unwanted animation when the viewport is resized. #32984

  • Fixed --bui-fg-success token in light mode to be more accessible. #32542

  • Deprecated the HeaderPage component name in favor of Header. The old HeaderPage, HeaderPageProps, HeaderPageOwnProps, HeaderPageBreadcrumb, and HeaderPageDefinition exports are still available as deprecated aliases. #33188

  • Added documentation for the table cell wrapper requirement to TSDoc comments for Cell, CellText, CellProfile, ColumnConfig, and RowRenderFn. #33379

  • Search Autocomplete Search Autocomplete Item Added SearchAutocomplete and SearchAutocompleteItem components for building accessible search-with-results patterns. Built on React Aria's Autocomplete with keyboard navigation and screen reader support. Designed for async/external search results with a configurable popover width. #33340

  • Checkbox Made Checkbox children optional and added a dev warning when neither a visible label, aria-label, nor aria-labelledby is provided. The label wrapper div is no longer rendered when there are no children, removing the unnecessary gap. #33394

  • Dialog Fixed Dialog content overflowing when no height prop is set. The dialog now grows with its content and scrolls when content exceeds the viewport height. #33352

  • Accordion Button Button Icon Button Link Card List Menu Select Toggle Button Group 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

  • Container Header Fixed incorrect bottom spacing caused by Container using padding-bottom for its default bottom spacing. Changed to margin-bottom and prevented it from applying when Container is used as the Header root element. #33354

  • Plugin Header Fixed PluginHeader to avoid triggering ResizeObserver loop completed with undelivered notifications warnings when used in layouts that react to the header height, such as pages that use FullPage. #33368

  • Tabs Plugin Header Fixed tab matchStrategy matching to ignore query parameters and hash fragments in tab href values. Previously, tabs with query params in their href (e.g., /page?group=foo) would never show as active since matching compared the full href string against location.pathname which never includes query params. #33047

  • Fixed Table component to use current --bui-bg-neutral-1 tokens instead of the removed --bui-bg-tint tokens, restoring row hover, selected, pressed, and disabled background colors. #33363

  • Fixed a bug in the useTable hook where the loading skeleton was never shown for complete mode when using getData. The initial data state was an empty array instead of undefined, causing the Table component to skip the loading state. #33378

  • Table Fixed Table column headers overflowing and wrapping when there is not enough space. Headers now truncate with ellipsis instead. #33256

  • Row Fixed Table row hover, selected, pressed, and disabled background states to use the correct neutral token level based on the container background. #33394

  • Row Fixed Table rows showing a pointer cursor when not interactive. Rows now only show cursor: pointer when they have an href, are selectable, or are pressable. #33256

  • Row Fixed Table rows with external href values to open in a new tab by automatically applying target="_blank" and rel="noopener noreferrer". #33353

  • Table Updated Table selection checkboxes to use aria-label instead of empty fragment children, improving accessibility and removing the unnecessary label gap in the selection cells. #33394

  • Button Button Icon Button Link 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

  • Link Fixed Link variant default from 'body' to 'body-medium' to match actual CSS selectors. The previous default did not correspond to a valid variant value. #33050

  • Menu Select Fixed scroll overflow in Menu and Select popover content when constrained by viewport height. #33049

  • Flex Grid Grid Item Added support for native HTML div attributes on the Flex, Grid, and Grid.Item components. #33136

  • Added virtualized prop to Table component for virtualized rendering of large datasets. Accepts true for default row height, { rowHeight: number } for fixed height, or { estimatedRowHeight: number } for variable height rows. #33246

  • Menu Select Fixed focus-visible outline styles for Menu and Select components. #32983

  • Fixed MenuItem onAction prop ordering so user-provided onAction handlers are chained rather than silently overwritten. #33150

  • Pages created with PageBlueprint now render the plugin header by default in the new frontend system. #33389

  • Select The Select trigger now automatically adapts its background colour based on the parent background context. #33102

  • Plugin Header Merged the internal PluginHeaderToolbar component into PluginHeader, removing the separate component and its associated types (PluginHeaderToolbarOwnProps, PluginHeaderToolbarProps) and definition (PluginHeaderToolbarDefinition). This is an internal refactor with no changes to the public API of PluginHeader. #33085

  • Updated dependency globals to ^17.0.0. #33251

  • Improved type safety in useDefinition by centralizing prop resolution and strengthening the BgPropsConstraint to require that bg provider components declare children as a required prop in their OwnProps type. #32971

  • Radio Group Added proper cursor styles for RadioGroup items. #32983

  • Card Fixed Card interactive cards not firing the onPress handler when clicking the card surface. #33343

  • Textfield Searchfield Password Field Fixed isRequired prop not being passed to the underlying React Aria field components in TextField, SearchField, and PasswordField. Previously, isRequired was consumed locally for the secondary label text but never forwarded, which meant the input elements lacked aria-required="true" and React Aria's built-in required validation was not activated. #33050

  • Searchfield Textfield SearchField and TextField now automatically adapt their background color based on the parent bg context, stepping up one neutral level (e.g. neutral-1 → neutral-2) when placed on a neutral background. TextField also gains a focus ring using the --bui-ring token. #33081

  • Improved useBreakpoint performance by sharing a single set of matchMedia listeners across all component instances instead of creating independent listeners per hook call. #33045

  • Alert Container Dialog Body Field Label Extended AlertProps, ContainerProps, DialogBodyProps, and FieldLabelProps with native div element props to allow passing attributes like aria-* and data-*. #33095

Version 0.12.0#

Breaking Changes#

  • Alert Breaking Alert no longer accepts a surface prop

    The Alert component's background is now driven entirely by its status prop. The surface prop has been removed.

    - <Alert surface="1" status="info" />
    + <Alert status="info" />
    ``` [#32711](https://github.com/backstage/backstage/pull/32711)
    
    
  • Breaking Removed gray scale tokens and renamed background surface tokens to neutral tokens

    The --bui-gray-1 through --bui-gray-8 tokens have been removed. The --bui-bg-surface-* and --bui-bg-neutral-on-surface-* tokens have been replaced by a unified --bui-bg-neutral-* scale. #32723

    Migration Guide:

    Replace surface tokens directly:

    - background: var(--bui-bg-surface-0);
    + background: var(--bui-bg-neutral-0);
    

    Replace on-surface tokens shifted by +1:

    - background: var(--bui-bg-neutral-on-surface-0);
    + background: var(--bui-bg-neutral-1);
    

    Replace gray tokens 1-4 with neutral equivalents (--bui-gray-5 through --bui-gray-8 have no direct replacement):

    - background: var(--bui-gray-1);
    + background: var(--bui-bg-neutral-1);
    
  • Box Button Button Icon Button Link Toggle Button Card Flex Grid 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 BREAKING: Renamed, added, and removed CSS tokens.

    • Renamed --bui-bg-neutral-0 to --bui-bg-app.
    • Renamed --bui-border to --bui-border-2.
    • Added --bui-border-1 for subtle, low-contrast borders.
    • Added --bui-bg-popover for the background color of popovers, tooltips, menus, and dialogs.
    • Removed --bui-border-hover, --bui-border-pressed, and --bui-border-disabled. #32837

    Migration Guide:

    - var(--bui-bg-neutral-0)
    + var(--bui-bg-app)
    
    - var(--bui-border)
    + var(--bui-border-2)
    

    Remove any references to --bui-border-hover, --bui-border-pressed, and --bui-border-disabled as these tokens no longer exist.

  • Button Icon Button Link 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)
    
    
  • Breaking The cell and header properties in ColumnConfig now return ReactElement instead of ReactNode.

    This fixes an issue where React Aria's Collection component would inject an id prop into Fragment wrappers, causing "Invalid prop id supplied to React.Fragment" errors on render.

    Migration:

    const columns: ColumnConfig<MyItem>[] = [
      {
        id: 'name',
        label: 'Name',
    -   cell: (item) => item.name,
    +   cell: (item) => <CellText title={item.name} />,
    -   header: () => 'Name',
    +   header: () => <Column>Name</Column>,
      },
    ];
    ``` [#32472](https://github.com/backstage/backstage/pull/32472)
    
    
  • Plugin Header Breaking Renamed the Header component to PluginHeader for clarity.

    The following exports have been renamed:

    • HeaderPluginHeader
    • HeaderPropsPluginHeaderProps
    • HeaderDefinitionPluginHeaderDefinition

    The HeaderTab type is unchanged as it is shared with HeaderPage.

    CSS class names have been updated from bui-Header* to bui-PluginHeader*. #32875

    Migration Guide:

    -import { Header, HeaderDefinition } from '@backstage/ui';
    +import { PluginHeader, PluginHeaderDefinition } from '@backstage/ui';
    
    -<Header title="My plugin" />
    +<PluginHeader title="My plugin" />
    
  • Button Breaking Removed large size variant from Button component as it was never implemented. #32541

    Migration Guide:

    - <Button size="large">Click me</Button>
    + <Button size="medium">Click me</Button>
    
  • Link Breaking Removed link and tint color tokens, added new status foreground tokens, and improved Link component styling

    The following color tokens have been removed:

    • --bui-fg-link (and all related tokens: -hover, -pressed, -disabled)
    • --bui-fg-tint (and all related tokens: -hover, -pressed, -disabled)
    • --bui-bg-tint (and all related tokens: -hover, -pressed, -disabled)
    • --bui-border-tint (and all related tokens)

    New Status Tokens:

    Added dedicated tokens for status colors that distinguish between usage on status backgrounds vs. standalone usage:

    • --bui-fg-danger-on-bg / --bui-fg-danger
    • --bui-fg-warning-on-bg / --bui-fg-warning
    • --bui-fg-success-on-bg / --bui-fg-success
    • --bui-fg-info-on-bg / --bui-fg-info

    The -on-bg variants are designed for text on colored backgrounds, while the base variants are for standalone status indicators with improved visibility and contrast. #32608

    Migration Guide:

    For link colors, migrate to one of the following alternatives:

    .custom-link {
    - color: var(--bui-fg-link);
    + color: var(--bui-fg-info);  /* For informational links */
    + /* or */
    + color: var(--bui-fg-primary);  /* For standard text links */
    }
    

    For tint colors (backgrounds, foregrounds, borders), migrate to appropriate status or neutral colors:

    .info-section {
    - background: var(--bui-bg-tint);
    + background: var(--bui-bg-info);  /* For informational sections */
    + /* or */
    + background: var(--bui-bg-neutral-1);  /* For neutral emphasis */
    }
    

    If you're using status foreground colors on colored backgrounds, update to the new -on-bg tokens:

    .error-badge {
    - color: var(--bui-fg-danger);
    + color: var(--bui-fg-danger-on-bg);
      background: var(--bui-bg-danger);
    }
    

Changes#

  • Full Page Added a new FullPage component that fills the remaining viewport height below the PluginHeader.

    <PluginHeader title="My Plugin" tabs={tabs} />
    <FullPage>
      {/* content fills remaining height */}
    </FullPage>
    ``` [#32822](https://github.com/backstage/backstage/pull/32822)
    
    
  • Fixed dark theme --bui-fg-secondary and --bui-fg-disabled tokens using black-based oklch(0% ...) instead of white-based oklch(100% ...), making secondary and disabled text visible on dark backgrounds. #32825

  • Box Fixed Box component to forward HTML attributes to the underlying div element. #32536

  • Use node prefix on native imports #32516

  • Accordion Fixed nested Accordion icon state issue where the inner accordion's arrow icon would incorrectly show as expanded when only the outer accordion was expanded. The CSS selector now uses a direct parent selector to ensure the icon only responds to its own accordion's expanded state. #32488

  • Popover Tooltip Fixed CSS Module syntax to comply with Next.js 16 Turbopack validation by flattening nested dark theme selectors. #32575

  • Button Added destructive prop to Button for dangerous actions like delete or remove. Works with all variants (primary, secondary, tertiary). #32554

  • Fixed useDefinition hook adding literal "undefined" class name when no className prop was passed. #32553

  • Tag Allow ref as a prop on the Tag component #32742

  • Cleaned up useDefinition ownProps types to remove never-typed ghost properties from autocomplete. #32799

  • Table Allow data to be passed directly to the useTable hook using the property data instead of getData() for mode "complete".

    This simplifies usage as data changes, rather than having to perform a useEffect when data changes, and then reloading the data. It also happens immediately, so stale data won't remain until a rerender (with an internal async state change), so less flickering. #32685

  • Table Fixed changing columns after first render from crashing. It now renders the table with the new column layout as columns change. #32684

  • Alert Added new Alert component with support for status variants (info, success, warning, danger), icons, loading states, and custom actions.

    Updated status color tokens for improved contrast and consistency across light and dark themes:

    • Added new --bui-bg-info and --bui-fg-info tokens for info status
    • Updated --bui-bg-danger, --bui-fg-danger tokens
    • Updated --bui-bg-warning, --bui-fg-warning tokens
    • Updated --bui-bg-success, --bui-fg-success tokens #32520
  • Tabs Tab Tag Group Tag Menu Menu Item Menu Autocomplete Fixed client-side navigation for container components by wrapping the container (not individual items) in RouterProvider. Components now conditionally provide routing context only when children have internal links, removing the Router context requirement when not needed. This also removes the need to wrap these components in MemoryRouter during tests when they are not using the href prop.

    Additionally, when multiple tabs match the current URL via prefix matching, the tab with the most specific path (highest segment count) is now selected. For example, with URL /catalog/users/john, a tab with path /catalog/users is now selected over a tab with path /catalog. #32373

  • Fixed an infinite render loop in Tabs when navigating to a URL that doesn't match any tab href. #32540

  • export PasswordField component #32643

  • Migrated Accordion components to use useDefinition instead of useStyles, and added automatic background adaptation based on parent container context. #32843

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

  • Updated react-router-dom peer dependency to ^6.30.2 and explicitly disabled v7 future flags to suppress deprecation warnings. #31818

  • Table The Table component now wraps the react-aria-components Table with a ResizableTableContainer only if any column has a width property set. This means that column widths can adapt to the content otherwise (if no width is explicitly set). #32686

  • Bump react-aria-components to v1.14.0 #32537

  • Fixed React 17 compatibility by using useId from react-aria instead of the built-in React hook which is only available in React 18+. #32428

  • Switch Fixed Switch component disabled state styling to show not-allowed cursor and disabled text color. #32551

  • Migrated to use the standard backstage-cli package build for CSS bundling instead of a custom build script. #32761

Version 0.11.0#

Breaking Changes#

  • Table Breaking Redesigned Table component with new useTable hook API.

    • The Table component (React Aria wrapper) is renamed to TableRoot
    • New high-level Table component that handles data display, pagination, sorting, and selection
    • The useTable hook is completely redesigned with a new API supporting three pagination modes (complete, offset, cursor)
    • New types: ColumnConfig, TableProps, TableItem, UseTableOptions, UseTableResult

    New features include unified pagination modes, debounced query changes, stale data preservation during reloads, and row selection with toggle/replace behaviors. #32050

    Migration Guide:

    1. Update imports and use the new useTable hook:
    -import { Table, useTable } from '@backstage/ui';
    -const { data, paginationProps } = useTable({ data: items, pagination: {...} });
    +import { Table, useTable, type ColumnConfig } from '@backstage/ui';
    +const { tableProps } = useTable({
    +  mode: 'complete',
    +  getData: () => items,
    +});
    
    1. Define columns and render with the new Table API:
    -<Table aria-label="My table">
    -  <TableHeader>...</TableHeader>
    -  <TableBody items={data}>...</TableBody>
    -</Table>
    -<TablePagination {...paginationProps} />
    +const columns: ColumnConfig<Item>[] = [
    +  { id: 'name', label: 'Name', isRowHeader: true, cell: item => <CellText title={item.name} /> },
    +  { id: 'type', label: 'Type', cell: item => <CellText title={item.type} /> },
    +];
    +
    +<Table columnConfig={columns} {...tableProps} />
    
  • Breaking Updating color tokens to match the new neutral style on different surfaces. #32202

    Migration Guide:

    There's no direct replacement for the old tint tokens but you can use the new neutral set of color tokens on surface 0 or 1 as a replacement.

    • --bui-bg-tint can be replaced by --bui-bg-neutral-on-surface-0
    • --bui-bg-tint-hover can be replaced by --bui-bg-neutral-on-surface-0-hover
    • --bui-bg-tint-pressed can be replaced by --bui-bg-neutral-on-surface-0-pressed
    • --bui-bg-tint-disabled can be replaced by --bui-bg-neutral-on-surface-0-disabled
  • Breaking Introduce new ToggleButton & ToggleButtonGroup components in Backstage UI #32232

  • Breaking Renamed CSS variable --bui-bg to --bui-bg-surface-0 for consistency. #32200

Changes#

  • Box Fixes app background color on dark mode. #32203

  • Checkbox Added indeterminate state support to the Checkbox component for handling partial selection scenarios like table header checkboxes. #32371

  • Select Added missing aria-label attributes to SearchField components in Select, MenuAutocomplete, and MenuAutocompleteListbox to fix accessibility warnings. #32337

  • Button Fixes disabled state in primary and secondary buttons in Backstage UI. #32297

  • build(deps-dev): bump storybook from 10.1.9 to 10.1.10 #32185

  • Button Fixed disabled tertiary buttons incorrectly showing hover effects on surfaces. #32385

  • Added new Popover component for Backstage UI with automatic overflow handling, and full placement support. Also introduced --bui-shadow token for consistent elevation styling across overlay components (Popover, Tooltip, Menu). #32313

  • Table Fixed Table sorting indicator not being visible when a column is actively sorted. #32350

  • Menu Fixed Menu component trigger button not toggling correctly. Removed custom click-outside handler that was interfering with React Aria's built-in state management, allowing the menu to properly open and close when clicking the trigger button. #32347

  • Table Added support for column width configuration in Table component. Columns now accept width, defaultWidth, minWidth, and maxWidth props for responsive layout control. #32336

  • Searchfield Fixed SearchField startCollapsed prop not working correctly in Backstage UI. The field now properly starts in a collapsed state, expands when clicked and focused, and collapses back when unfocused with no input. Also fixed CSS logic to work correctly in all layout contexts (flex row, flex column, and regular containers). #32123

  • Fixed Link component causing hard page refreshes for internal routes. The component now properly uses React Router's navigation instead of full page reloads. #32265

  • Table Added support for custom pagination options in useTable hook and Table component. You can now configure pageSizeOptions to customize the page size dropdown, and hook into pagination events via onPageSizeChange, onNextPage, and onPreviousPage callbacks. When pageSize doesn't match any option, the first option is used and a warning is logged. #32321

  • Table Fixed missing border styles on table selection cells in multi-select mode. #32369

  • Table Added className and style props to the Table component. #32342

Version 0.10.0#

Breaking Changes#

  • Breaking The Cell component has been refactored to be a generic wrapper component that accepts children for custom cell content. The text-specific functionality (previously part of Cell) has been moved to a new CellText component. #31917

    Migration Guide:

    If you were using Cell with text-specific props (title, description, leadingIcon, href), you need to update your code to use CellText instead:

    Before:

    <Cell
      title="My Title"
      description="My description"
      leadingIcon={<Icon />}
      href="/path"
    />
    

    After:

    <CellText
      title="My Title"
      description="My description"
      leadingIcon={<Icon />}
      href="/path"
    />
    

    For custom cell content, use the new generic Cell component:

    <Cell>{/* Your custom content */}</Cell>
    

Changes#

  • Checkbox Fixed Checkbox indicator showing checkmark color when unchecked. #31904

  • Button Icon Fixed ButtonIcon incorrectly applying className to inner elements instead of only the root element. #31900

  • Fixed Table Row component to correctly handle cases where no href is provided, preventing unnecessary router provider wrapping and fixing the cursor incorrectly showing as a pointer despite the element not being a link. #31843

  • Table Added row selection support with visual state styling for hover, selected, and pressed states. Fixed checkbox rendering to only show for multi-select toggle mode. #31907

  • Fixed useTable hook to prioritize providedRowCount over data length for accurate row count in server-side pagination scenarios. #31817

  • Fixed Table column sorting indicator to show up arrow when no sort is active, correctly indicating that clicking will sort ascending. #31844

Version 0.9.1#

Changes#

  • Fixed Table Row component to correctly handle cases where no href is provided, preventing unnecessary router provider wrapping and fixing the cursor incorrectly showing as a pointer despite the element not being a link. #31843

  • Fixed useTable hook to prioritize providedRowCount over data length for accurate row count in server-side pagination scenarios. #31817

  • Fixed Table column sorting indicator to show up arrow when no sort is active, correctly indicating that clicking will sort ascending. #31844

Version 0.9.0#

Breaking Changes#

  • Avatar Breaking Migrated Avatar component from Base UI to custom implementation with size changes:

    • Base UI-specific props are no longer supported
    • Size values have been updated:
      • New x-small size added (1.25rem / 20px)
      • small size unchanged (1.5rem / 24px)
      • medium size unchanged (2rem / 32px, default)
      • large size changed from 3rem to 2.5rem (40px)
      • New x-large size added (3rem / 48px) #31566

    Migration Guide:

    # Remove Base UI-specific props
    - <Avatar src="..." name="..." render={...} />
    + <Avatar src="..." name="..." />
    
    # Update large size usage to x-large for same visual size
    - <Avatar src="..." name="..." size="large" />
    + <Avatar src="..." name="..." size="x-large" />
    

    Added purpose prop for accessibility control ('informative' or 'decoration').

  • Breaking Migrated Checkbox component from Base UI to React Aria Components.

    API changes required:

    • checkedisSelected
    • defaultCheckeddefaultSelected
    • disabledisDisabled
    • requiredisRequired
    • label prop removed - use children instead
    • CSS: bui-CheckboxLabel class removed
    • Data attribute: data-checkeddata-selected
    • Use without label is no longer supported #31517

    Migration Guide:

    Before:

    <Checkbox label="Accept terms" checked={agreed} onChange={setAgreed} />
    

    After:

    <Checkbox isSelected={agreed} onChange={setAgreed}>
      Accept terms
    </Checkbox>
    

    Before:

    <Checkbox label="Option" disabled />
    

    After:

    <Checkbox isDisabled>Option</Checkbox>
    

    Before:

    <Checkbox />
    

    After:

    <Checkbox>
      <VisuallyHidden>Accessible label</VisuallyHidden>
    </Checkbox>
    
  • Breaking Fixing styles on SearchField in Backstage UI after migration to CSS modules. SearchField has now its own set of class names. We previously used class names from TextField but this approach was creating some confusion so going forward in your theme you'll be able to theme TextField and SearchField separately. #31507

  • Breaking Removed central componentDefinitions object and related type utilities (ComponentDefinitionName, ComponentClassNames).

    Component definitions are primarily intended for documenting the CSS class API for theming purposes, not for programmatic use in JavaScript/TypeScript. #31744

    Migration Guide:

    If you were using component definitions or class names to build custom components, we recommend migrating to either:

    • Use Backstage UI components directly as building blocks, or
    • Duplicate the component CSS in your own stylesheets instead of relying on internal class names
  • Menu Switch Skeleton Plugin Header Header Tabs Breaking Changed className prop behavior to augment default styles instead of being ignored or overriding them.

    If you were passing custom className values to any of these components that relied on the previous behavior, you may need to adjust your styles to account for the default classes now being applied alongside your custom classes. #31496

  • Breaking Removed Collapsible component. Migrate to Accordion or use React Aria Disclosure. #31493

    Migration Guide:

    Path 1: Accordion (Opinionated Styled Component)

    Accordion provides preset styling with a similar component structure.

    - import { Collapsible } from '@backstage/ui';
    + import { Accordion, AccordionTrigger, AccordionPanel } from '@backstage/ui';
    
    - <Collapsible.Root>
    -   <Collapsible.Trigger render={(props) => <Button {...props}>Toggle</Button>} />
    -   <Collapsible.Panel>Content</Collapsible.Panel>
    - </Collapsible.Root>
    
    + <Accordion>
    +   <AccordionTrigger title="Toggle" />
    +   <AccordionPanel>Content</AccordionPanel>
    + </Accordion>
    

    CSS classes: .bui-CollapsibleRoot.bui-Accordion, .bui-CollapsibleTrigger.bui-AccordionTrigger (now on heading element), .bui-CollapsiblePanel.bui-AccordionPanel

    Path 2: React Aria Disclosure (Full Customization)

    For custom styling without preset styles:

    import { Disclosure, Button, DisclosurePanel } from 'react-aria-components';
    
    <Disclosure>
      <Button slot="trigger">Toggle</Button>
      <DisclosurePanel>Content</DisclosurePanel>
    </Disclosure>;
    
  • Select Breaking The SelectProps interface now accepts a generic type parameter for selection mode.

    Added searchable and multiple selection support to Select component. The component now accepts searchable, selectionMode, and searchPlaceholder props to enable filtering and multi-selection modes. #31649

    Migration Guide:

    If you're using SelectProps type directly, update from SelectProps to SelectProps<'single' | 'multiple'>. Component usage remains backward compatible.

Changes#

  • Plugin Header Fix broken external links in Backstage UI PluginHeader component. #31525

  • Select Fixed CSS issues in Select component including popover width constraints, focus outline behavior, and overflow handling. #31618

  • Password Field Searchfield Improved visual consistency of PasswordField, SearchField, and MenuAutocomplete components. #31679

  • Text Fix default text color in Backstage UI #31429

  • Text Fixed Text component to prevent truncate prop from being spread to the underlying DOM element. #31615

  • Link Improved the Link component structure in Backstage UI. #31524

  • Dialog Fixed dialog backdrop appearance in dark mode. #31673

  • Avatar Migrated CellProfile component from Base UI Avatar to Backstage UI Avatar component. #31608

  • Avatar Avatar components in x-small and small sizes now display only one initial instead of two, improving readability at smaller dimensions. #31623

  • Removed @base-ui-components/react dependency as all components now use React Aria Components. #31672

  • Fix the default font size in Backstage UI. #31435

  • Fix CSS layer ordering in Backstage UI to make sure component styles are loaded after tokens and base declarations. #31448

  • Radio Group Fixed RadioGroup radio button ellipse distortion by preventing flex shrink and grow. #31576

  • Fix font smoothing as default in Backstage UI. #31444

  • Enable tree-shaking of imports other than *.css. #31516

  • Button Button Icon Added loading prop to Button and ButtonIcon components for displaying spinner during async operations. #31681

  • Table Fixed Table Row component to properly support opening links in new tabs via right-click or Cmd+Click when using the href prop. #31680

  • Set the color-scheme property depending on theme #31469

  • Visually Hidden Added new VisuallyHidden component for hiding content visually while keeping it accessible to screen readers. #31484

  • Fix default font weight and font family in Backstage UI. #31432

Version 0.8.2#

Changes#

  • Fix default text color in Backstage UI #31429

  • Fix the default font size in Backstage UI. #31435

  • Fix CSS layer ordering in Backstage UI to make sure component styles are loaded after tokens and base declarations. #31448

  • Fix font smoothing as default in Backstage UI. #31444

  • Fix default font wight and font family in Backstage UI. #31432

Version 0.8.0#

Breaking Changes#

  • Breaking Added a new PasswordField component. As part of this change, the password and search types have been removed from TextField. #31238

  • Breaking Restructure Backstage UI component styling to use CSS Modules instead of pure CSS. We don't expect this to be an issue in practice but it is important to call out that all styles are now loaded through CSS modules with generated class names. We are still providing fixed class names for all components to allow anyone to style their Backstage instance. #31399

  • Breaking The ScrollArea component has been removed from Backstage UI because it did not meet our accessibility standards. #31409

  • Breaking Remove Icon component in Backstage UI. This component was creating issue for tree-shaking. It is recommended to use icons from @remixicon/react until we found a better alternative in Backstage UI. #31407

Changes#

  • Adding a new Dialog component to Backstage UI. #31371

  • remove default selection of tab #31216

  • Fix margin utility classes in Backstage UI. #31389

  • Fix scroll jumping when opening menu in Backstage UI. #31394

  • Making href mandatory in tabs that are part of a Header component #31343

  • Update react-aria-components to version 1.13.0 #31367

  • Fix table sorting icon position in Backstage UI. #31393

  • Add new virtualized, maxWidth and maxHeight props to Menu, MenuListBox, MenuAutocomplete and MenuAutocompleteListBox to allow for virtalization of long lists inside menus. #31375

  • Added support for data attributes in <Box />, <Container />, <Flex />, and <Grid /> components, ensuring they are correctly applied to the rendered elements. #31374

  • Cleaning up Backstage UI props definitions as well as removing ScrollArea in Card to improve accessibility. #31404

  • Add react router for internal routing for ButtonLinks #31276

  • Added a background color default on the body #31365

  • We are restructuring our CSS to have a better layer structure. #31362

  • Improved SearchField component flex layout and animations. Fixed SearchField behavior in Header components by switching from width-based transitions to flex-basis transitions for better responsive behavior. Added new Storybook stories to test SearchField integration with Header component. #31158

  • Remove auto selection of tabs for tabs that all have href defined #31281

  • Avoid overriding onChange when spreading props #31232

  • Using react router for internal links in the Menu component #31339

Version 0.7.2#

Changes#

  • Making href mandatory in tabs that are part of a Header component #31343

  • Add react router for internal routing for ButtonLinks #31276

  • Remove auto selection of tabs for tabs that all have href defined #31281

  • Using react router for internal links in the Menu component #31339

  • Enable tooltips on disabled buttons with automatic wrapper #31230

  • Avoid overriding onChange when spreading props #31232

  • remove default selection of tab #31216

  • Improved SearchField component flex layout and animations. Fixed SearchField behavior in Header components by switching from width-based transitions to flex-basis transitions for better responsive behavior. Added new Storybook stories to test SearchField integration with Header component. #31158

Version 0.7.1#

Changes#

  • Add missing class for flex: baseline #31013

  • Fix Select component to properly attach aria-label and aria-labelledby props to the rendered element for improved accessibility. #31037

  • Removed the need to mock window.matchMedia in tests, falling back to default breakpoint values instead. #31148

Version 0.7.0#

Breaking Changes#

  • Breaking We are moving our DataTable component to React Aria. We removed our DataTable to only use Table as a single and opinionated option for tables. This new structure is made possible by using React Aria under the hood. #30654

  • Breaking Backstage UI - HeaderPage - We are updating the breadcrumb to be more visible and accessible. #30874

  • Breaking We are updating the Menu component to use React Aria under the hood. The structure and all props are changing to follow React Aria's guidance. #30908

  • Breaking We are upgrading our Text component to support all font sizes making the Heading component redundant. The new Text component introduces 4 sizes for title and 4 sizes for body text. All of these work in multiple colors and font weights. We improved the as prop to include all possible values. The Link component has also been updated to match the new Text component. #30592

Changes#

  • Fixes some styles on the Select component in BUI. #30642

  • Export CardHeader, CardBody and CardFooter from Card component index #30882

  • Add new TagGroup component to Backstage UI. #30919

  • Fixes a couple of small bugs in BUI including setting H1 and H2 correctly on the Header and HeaderPage. #30636

  • Update styling of Tooltip element #30591

  • Move breadcrumb to fit in the HeaderPage instead of the Header in Backstage UI. #30701

  • We are motion away from motion to use gsap instead to make Backstage UI backward compatible with React 17. #30626

  • Updated Menu component in Backstage UI to use useId() from React Aria instead of React to support React 17. #30675

  • Remove stylesheet import from Select component. #30800

  • Add startCollapsed prop on the SearchField component in BUI. #30729

  • Adds onTabSelectionChange to ui header component. #30588

Version 0.6.0#

Breaking Changes#

  • Breaking Canon has been renamed to Backstage UI. This means that @backstage/canon has been deprecated and replaced by @backstage/ui. #30525

Version 0.5.0#

Breaking Changes#

  • Breaking We are updating the default size of the Button component in Canon to be small instead of medium. #30085

  • Breaking We set the default size for IconButton in Canon to be small instead of medium. #30097

  • Breaking Move TextField component to use react Aria under the hood. Introducing a new FieldLabel component to help build custom fields. #30286

  • Breaking We are adding a new as prop on the Heading and Text component to make it easier to change the component tag. We are removing the render prop in favour of the as prop. #30291

  • Breaking TextField in Canon now has multiple label sizes as well as the capacity to hide label and description but still make them available for screen readers. #30249

  • Breaking Fixes spacing props on layout components and aligned on naming for the Grid component. You should now call the Grid root component using <Grid.Root /> instead of just <Grid />. #30013

Changes#

  • Add min-width: 0; by default on every Flex components in Canon to help support truncated texts inside flex elements. #30168

  • Fix styling for the title4 prop on the Heading component in Canon. #30167

  • Added a render prop to the Button component in Canon to use it as a link. #30165

  • Add new Switch component in Canon. #30251

  • The filter input in menu comboboxes should now always use the full width of the menu it's in. #30104

  • Remove leftover console.log from Container component. #30040

Version 0.4.0#

Breaking Changes#

  • Breaking Icons on Button and IconButton now need to be imported and placed like this: <Button iconStart={<ChevronDownIcon />} /> #29667

  • Breaking We are modifying the way we treat custom render using 'useRender()' under the hood from BaseUI. #29989

  • Breaking The icon prop in TextField now accept a ReactNode instead of an icon name. We also updated the icon sizes for each input sizes. #29974

Changes#

  • Use correct colour token for TextField clear button icon, prevent layout shift whenever it is hidden or shown and properly size focus area around it. Also stop leading icon shrinking when used together with clear button. #29878

  • Fix Canon missing dependencies #29642

  • For improved a11y, clicking a Select component label now focuses the Select trigger element, and the TextField component's label is now styled to indicate it's interactive. #29755

  • Added new icon and onClear props to the TextField to make it easier to accessorize inputs. #29820

  • Add new Tabs component to Canon #29996

  • Pin version of @base-ui-components/react. #29782

  • Fixed an issue with Canon's DataTable.Pagination component showing the wrong number for the "to" count. #29688

  • Removed various typos #29665

  • Update Menu component in Canon to make the UI more condensed. We are also adding a new Combobox option for nested navigation. #29986

  • Use the Field component from Base UI within the TextField. #29826

  • Add new truncate prop to Text and Heading components in Canon. #29988

Version 0.3.2#

Changes#

  • Fix Canon missing dependencies #29642

Version 0.3.0#

Breaking Changes#

  • Breaking Improve class name structure using data attributes instead of class names. #29560

  • Breaking Updated TextField and Select component to work with React Hook Form. #29482

  • Breaking Add new Select component for Canon #29440

  • Breaking Added a new TextField component to replace the Field and Input component. After feedback, it became clear that we needed to build a more opinionated version to avoid any problem in the future. #29364

Changes#

  • Updated styles for the Menu component in Canon. #29351

  • Fix Checkbox styles on dark theme in Canon. #29544

  • Add new breakpoint helpers up(), down() and current breakpoint to help you use our breakpoints in your React components. #29564

  • Internal refactor and fixes to the prop extraction logic for layout components. #29389

  • Add new Collapsible component for Canon. #29617

  • Removes instances of default React imports, a necessary update for the upcoming React 19 migration.

    https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html #29499

  • Add global CSS reset for anchor tags. #29357

  • Improved Container styles, changing our max-width to 120rem and improving padding on smaller screens. #29475

  • Add new Avatar component to Canon. #29594

  • Add new TableCellProfile component for Table and DataTable in Canon. #29600

  • Fix types on the Icon component. #29306

  • Add new DataTable component and update Table component styles. #29484

  • Move styles to the root of the TextField component. #29466

  • We added a render prop to the Link component to make sure it can work with React Router. #29247

  • Fix Select styles on small sizes + with long option names in Canon. #29545

  • Added a new gray scale for Canon for both light and dark theme. #29543

  • Add support for column sizing in DataTable. #29603

  • Fix the Icon component when the name is not found to return null instead of an empty SVG. #29280

Version 0.2.1#

Changes#

  • Internal refactor and fixes to the prop extraction logic for layout components. #29389

  • Fix types on the Icon component. #29306

  • Updated styles for the Menu component in Canon. #29351

  • Add global CSS reset for anchor tags. #29357

  • Fix the Icon component when the name is not found to return null instead of an empty SVG. #29280

Version 0.2.0#

Breaking Changes#

  • Breaking Fix CSS imports and move CSS outputs out of the dist folder. #28961

  • Breaking Added a new Tooltip component to Canon. #29241

  • Breaking We added a new IconButton component with fixed sizes showcasing a single icon. #29239

  • Breaking Added about 40 new icons to Canon. #29264

  • Breaking We are renaming CanonProvider to IconProvider to improve clarity on how to override icons. #29002

  • Breaking Added a new Menu component to Canon. #29151

  • Breaking Updating styles for Text and Link components as well as global surface tokens. #29200

  • Breaking Added a new ScrollArea component for Canon. #29240

Changes#

  • Fix Button types that was preventing the use of native attributes like onClick. #29205

  • To avoid conflicts with Backstage, we removed global styles and set font-family and font-weight for each components. #28972

  • Introducing Canon to Backstage. Canon styling system is based on pure CSS. We are adding our styles.css at the top of your Backstage instance. #29137

Version 0.1.0#

Breaking Changes#

  • Breaking Merged the Stack and Inline component into a single component called Flex. #28634

  • Breaking This is the first alpha release for Canon. As part of this release we are introducing 5 layout components and 7 components. All theming is done through CSS variables. #28562

  • Breaking Fixing css structure and making sure that props are applying the correct styles for all responsive values. #28630

  • Breaking Updated core CSS tokens and fixing the Button component accordingly. #28789

Changes#

  • Removed client directive as they are not needed in React 18. #28626

  • Fix spacing props not being applied for custom values. #28770

  • Removed older versions of React packages as a preparatory step for upgrading to React 19. This commit does not introduce any functional changes, but removes dependencies on previous React versions, allowing for a cleaner upgrade path in subsequent commits. #28579

Version 0.1.0#

We're excited to share the initial release of Backstage UI 💚 In this first alpha version, you'll find the foundation of our design system: a set of versatile layout components and a handful of essential atomic elements to help you get started. While Backstage UI is still in its early stages, it's ready for exploration and we'd love for you to give it a try and share your feedback.