name: ui description: How to build UI in this repo — the bui (@backstage/ui) design system vs legacy @backstage/core-components + MUI v4, which to reach for, and how to read Backstage Storybook component/story source. Use when creating or changing pages, cards, layouts, buttons, or any frontend component.
The three UI layers in this repo
| Layer | Import from | Status |
| --- | --- | --- |
| bui — the new Backstage design system | @backstage/ui (aka "Backstage UI", "BUI") | Preferred for new work. The direction we're migrating toward. |
| core-components — classic Backstage components | @backstage/core-components | Legacy but still required where bui has no equivalent (e.g. feature-rich Table, Page/Header/Content scaffolding, Link with route refs). |
| Material UI v4 | @material-ui/core | Legacy primitives + makeStyles. Used for styling and gaps bui doesn't cover. MUI v4 docs. |
Rule of thumb: reach for bui first. Fall back to core-components / MUI v4 only
when bui lacks the piece you need. Mixing all three in one file is normal and
expected during the migration — see ClusterAboutCard.tsx, which imports Grid
from bui, Link from core-components, and Box/Tooltip from MUI v4 together.
@backstage/ui is currently 0.16.0 — a young, fast-moving package. APIs change
between releases and it is not a full replacement for core-components yet.
When unsure whether a bui component exists or what props it takes, check the
Storybook source (see below) rather than guessing.
bui setup (already done)
- The plugin's
package.jsonmust declare"@backstage/ui": "backstage:^"(already present ings,ui-react,ai-chat, andapp). - The global stylesheet is imported once in
packages/app/src/index.tsx:import '@backstage/ui/css/styles.css';. Don't re-import it per component. - We do not wrap the app in
BUIProvider— bui components render against the existing app theme + the global CSS. (Storybook recipes useBUIProviderbecause they render in isolation; the real app doesn't need it.)
bui components we actually use
Common primitives already in the codebase (import from @backstage/ui):
- Layout:
Flex,Grid,Box,Container - Surfaces:
Card,CardHeader,CardBody,CardFooter - Typography:
Text(e.g.<Text as="h3" variant="title-x-small" weight="bold">) - Controls:
Button,ButtonIcon,Switch,Link - Overlay/menu:
Tooltip,TooltipTrigger,MenuTrigger,Menu,MenuItem - Data display:
Table+Cell/CellText/ColumnConfig,List,ListRow,Avatar - Page chrome:
PluginHeader,Header(the two-tier header pattern — plugin-levelPluginHeaderabove an entity-levelHeader). NFS page headers andSubPageBlueprinttabs are rendered by the customGSPageLayoutswappable component, not directly — see "Page headers and tabs" indocs/ui.mdbefore building a tabbed page.
Cards: use the shared InfoCard wrapper
@giantswarm/backstage-plugin-ui-react exports an InfoCard built on bui's
Card/CardHeader/CardBody/CardFooter with our standard title styling and
header/footer action slots. Prefer it over hand-rolling a bui Card or the
core-components InfoCard for new cards. Source:
plugins/ui-react/src/components/InfoCard/InfoCard.tsx.
Tables: see the tables skill
The bui Table (data-driven columnConfig + data, cells must return
Cell/CellText) and the feature-rich core-components Table are both
documented in depth in the tables skill, including the choice matrix and
gotchas (loading skeleton needs data={undefined}). Read that skill for anything
table-related; don't duplicate it here.
Reading Backstage Storybook source
The Backstage Storybook renders components
from the upstream backstage/backstage monorepo — stories are .stories.tsx
files colocated with their components. There is no separate storybook repo,
and we do not run a local storybook. To learn a pattern (how a page/card is
composed, what props a bui component takes), read the story + component source
directly.
The reliable technique: index.json → importPath → GitHub raw
Every Storybook deployment publishes a machine-readable index at
https://backstage.io/storybook/index.json. Each entry carries the exact source
paths:
{
"id": "recipes-pluginheader-and-header--with-tabs",
"title": "Recipes/PluginHeader and Header",
"importPath": "./packages/ui/src/recipes/PluginHeaderAndHeader.stories.tsx",
"componentPath": "./packages/ui/src/components/PluginHeader/PluginHeader.tsx"
}
Then fetch the raw source from GitHub (strip the leading ./):
BASE=https://raw.githubusercontent.com/backstage/backstage/master
curl -sfL "$BASE/packages/ui/src/recipes/PluginHeaderAndHeader.stories.tsx"
curl -sfL "$BASE/packages/ui/src/components/PluginHeader/PluginHeader.tsx"
To find the story ID for a Storybook URL: the ?path=/story/<id> query param
is the entry id. Story IDs can drift between releases (the "Recipes/..."
example also now exists as "Backstage UI/PluginHeader") — search index.json by
title or a keyword rather than trusting an old URL. Quick lookup:
curl -sfL https://backstage.io/storybook/index.json \
| jq '.entries | to_entries[] | select(.value.title|test("PluginHeader";"i")) | .value | {id,importPath,componentPath}'
Live inspection with Chrome DevTools
Use the chrome-devtools MCP when you want to inspect the rendered result
(DOM, computed styles, a11y tree) or grab index.json without CORS friction:
new_page/navigate_pagetohttps://backstage.io/storybook/evaluate_scriptrunningfetch('https://backstage.io/storybook/index.json')(same-origin, so no CORS block) to pull the index or any raw story texttake_snapshotfor the a11y tree of a rendered story
Note: the chrome-devtools MCP has its own filesystem roots — it can't write into
the session scratchpad. Fetch text via evaluate_script and return it inline, or
just curl the GitHub raw URL from Bash instead.
Related skills
tables— buiTablevs core-componentsTable, columns, filtering, faceted sidebars.components— component directory structure, named exports, barrel files.upstream-search— searching a local clone ofbackstage/backstage(needsBACKSTAGE_UPSTREAM_DIRset; the Storybook technique above needs no clone).
Next.js App Router Expert
Development
A skill that turns Claude into a Next.js App Router expert.
README Generator
Development
Creates professional and comprehensive README.md files for your projects.
API Documentation Writer
Development
Generates comprehensive API documentation in OpenAPI/Swagger format.