$npx -y skills add redhat-developer/rhdh-skill --skill rhdh-codingBackstage and RHDH plugin development patterns. Use when writing, modifying, or reviewing code in a Backstage or RHDH plugin — frontend components, backend services, API clients, styling, testing, entity pages, scaffolder actions, catalog processors, NFS Blueprints, dynamic plugi
| 1 | # RHDH Coding |
| 2 | |
| 3 | Patterns for Backstage and RHDH plugin development that agents need but can't |
| 4 | reliably get from training data or codebase discovery alone. This covers what |
| 5 | you'd learn after six months of getting burned by non-obvious conventions. |
| 6 | |
| 7 | ## Before You Write Code |
| 8 | |
| 9 | ### 1. Check for existing specs |
| 10 | |
| 11 | Look for a spec, PRD, or OpenSpec design for this work — in `docs/plans/**/`, |
| 12 | `specifications/`, `openspec/changes/*/`, or linked from the issue. If found, |
| 13 | use the component list and acceptance criteria as your implementation blueprint. |
| 14 | Read `references/frontend-specs.md` for what good frontend specs include. |
| 15 | |
| 16 | ### 2. Discover the plugin context |
| 17 | |
| 18 | Run the detection script to understand what you're working with: |
| 19 | |
| 20 | ```bash |
| 21 | python scripts/detect-rhdh-context.py --path <plugin-dir> |
| 22 | ``` |
| 23 | |
| 24 | This reports: Backstage role, frontend system (legacy/NFS/dual), existing |
| 25 | extensions, MUI version, dynamic plugin status, plugin ID, scalprum name. |
| 26 | |
| 27 | ### 3. Read workspace instructions |
| 28 | |
| 29 | ```bash |
| 30 | test -f AGENTS.md && cat AGENTS.md |
| 31 | test -f CLAUDE.md && cat CLAUDE.md |
| 32 | ``` |
| 33 | |
| 34 | These contain repo-specific rules that override general patterns. |
| 35 | |
| 36 | ### 4. Check version compatibility |
| 37 | |
| 38 | Consult `../rhdh/references/versions.md` for the RHDH → Backstage version |
| 39 | matrix. Your `@backstage/*` dependency versions must match the target RHDH |
| 40 | version. Mismatched versions cause runtime errors — most commonly "Cannot |
| 41 | read properties of undefined." |
| 42 | |
| 43 | ## Styling: BUI First |
| 44 | |
| 45 | **For new plugins,** use Backstage UI (`@backstage/ui`) with CSS Modules and |
| 46 | BUI CSS variables. **In existing plugins,** match whatever the workspace already |
| 47 | uses — if it's MUI v4, stay consistent rather than mixing libraries. Only |
| 48 | introduce BUI in a workspace that has already adopted it or is actively migrating. |
| 49 | |
| 50 | **Priority for new plugins:** |
| 51 | 1. **BUI** (`@backstage/ui`) — default for new plugins and new workspaces |
| 52 | 2. **MUI v5** (`@mui/material`) — when BUI lacks the component you need |
| 53 | 3. **MUI v4** (`@material-ui/core`) — legacy maintenance only |
| 54 | |
| 55 | When using MUI v5 alongside BUI, add the class name generator to prevent |
| 56 | collisions in dynamic plugin bundles: |
| 57 | |
| 58 | ```typescript |
| 59 | // src/index.ts |
| 60 | import { unstable_ClassNameGenerator as ClassNameGenerator } from '@mui/material/className'; |
| 61 | ClassNameGenerator.configure(name => name.startsWith('v5-') ? name : `v5-${name}`); |
| 62 | ``` |
| 63 | |
| 64 | **Icons:** Use `@remixicon/react` (not `@material-ui/icons`). |
| 65 | |
| 66 | Read `references/bui.md` for the component mapping table and CSS variable reference. |
| 67 | |
| 68 | ## Frontend Implementation |
| 69 | |
| 70 | ### Verify BUI component APIs before use |
| 71 | |
| 72 | The `references/bui.md` mapping table is a quick-start guide, not the source |
| 73 | of truth. Before using any BUI component for the first time, check the actual |
| 74 | type definitions in `node_modules/@backstage/ui/dist/index.d.ts`. Patterns |
| 75 | that differ from what you might expect: |
| 76 | |
| 77 | - `Card` uses a discriminated union (href/onPress/static) — no `onClick` |
| 78 | - `Select` multi-mode uses `value`/`onChange`, not `selectedKeys`/`onSelectionChange` |
| 79 | - `Table` requires `isRowHeader: true` on at least one column config |
| 80 | - `Badge` has no `variant` prop |
| 81 | |
| 82 | ### Follow existing repo conventions |
| 83 | |
| 84 | Before creating any configuration, fixture path, or utility pattern, check |
| 85 | 2–3 other workspaces in the repo for the established convention. Discoveries |
| 86 | that save debugging time: |
| 87 | |
| 88 | - Catalog `type: file` paths resolve from `packages/backend/` CWD — use |
| 89 | `../../` to reach workspace root files |
| 90 | - Frontend plugin `.eslintrc.js` may need `root: true` to avoid monorepo |
| 91 | plugin conflicts — check sibling packages before changing |
| 92 | - `import React from 'react'` is blocked — use named imports |
| 93 | |
| 94 | ### Use CLI tools when specs say to |
| 95 | |
| 96 | If the task spec says "scaffold via backstage-cli" or "run create-app", use |
| 97 | those tools. Do not silently substitute manual file creation. If the CLI fails, |
| 98 | report the error and ask for guidance. |
| 99 | |
| 100 | ### Keep hooks simple |
| 101 | |
| 102 | Data-fetching hooks should fetch once and apply filters via `useMemo`. Do not |
| 103 | split server-side and client-side filter concerns unless the spec explicitly |
| 104 | identifies a dataset size that requires server-side pagination. Do not suppress |
| 105 | `react-hooks/exhaustive-deps` as a first approach — if you need a suppression, |
| 106 | the hook design is probably too complex. |
| 107 | |
| 108 | ### Build incrementally, not in bulk |
| 109 | |
| 110 | Do not write all components in one pass and then run CI. Write one component |
| 111 | or hook, run `yarn tsc:full` to verify types, and i |