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).
Expert Next.js App Router
Developpement
Un skill qui transforme Claude en expert Next.js App Router.
Générateur de README
Developpement
Crée des README.md professionnels et complets pour vos projets.
Rédacteur de Documentation API
Developpement
Génère de la documentation API complète au format OpenAPI/Swagger.