From rhdh
Migrate Backstage frontend plugins from the legacy system to the New Frontend System (NFS) using alpha exports and blueprints.
How this skill is triggered — by the user, by Claude, or both
Slash command
/rhdh:nfs-migrationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<essential_principles>
examples/before-after-drawer.mdexamples/before-after-entity.mdexamples/before-after-page.mdreferences/api-changes.mdreferences/app-setup.mdreferences/gotchas.mdreferences/migrate-app-level.mdreferences/migrate-entity-content.mdreferences/migrate-page.mdreferences/migrate-rhdh-extensions.mdreferences/migrate-translations.mdreferences/mount-point-mapping.mdreferences/operator-config.mdreferences/overview.mdreferences/package-json.mdreferences/reference-prs.mdreferences/support.mdreferences/testing-rhdh.mdreferences/verification.mdworkflows/test-nfs-plugin.md<essential_principles>
Always read the plugin's `package.json`, `src/plugin.ts` (or `src/plugin.tsx`), route refs, API factories, and exported components before making any changes. Understand what exists before migrating. NFS is not GA yet. The default approach is to add NFS at `./alpha` while keeping legacy at the root export (`.`). This avoids breaking existing consumers. Use `@backstage/frontend-plugin-api` for core blueprints. RHDH-specific blueprints (`AppDrawerContentBlueprint`, `GlobalHeaderMenuItemBlueprint`) come from `@red-hat-developer-hub/*` packages. Don't mix them up. Entity content and cards can go directly in the plugin's `extensions` array — the blueprint declares its own attach point. Use `createFrontendModule` only for extensions that target a different plugin (translations → `pluginId: 'app'`, homepage widgets → `pluginId: 'home'`) or when injecting content from outside a plugin you don't own. Keep component imports (`useApi`, `useRouteRef`, etc.) on `@backstage/core-plugin-api` — they work in both legacy and NFS contexts. This lets the same components serve both export paths. Only use `compatWrapper()` when a component depends on legacy context providers (e.g. old `SidebarContext`) that aren't available in NFS. Don't migrate component imports to `@backstage/frontend-plugin-api` if you need to support legacy consumers. Legacy exports must remain available since NFS is not GA. With the alpha approach, legacy stays at root unchanged. With the colocated approach, legacy source moves to `legacy.ts` but is re-exported from `index.ts` so existing consumers don't break.</essential_principles>
Wait for response before proceeding.
| Response | Action |
|---|---|
| 1, "migrate", "convert", "NFS" | Follow the migration workflow below |
| 2, "test", "verify", "deploy" | Read workflows/test-nfs-plugin.md |
| 3, "learn", "guide", "overview" | Read references/overview.md and present key sections to the user |
<migration_workflow>
Read package.json and src/plugin.ts (or src/plugin.tsx). Identify:
app-config.dynamic.yaml — see references/mount-point-mapping.md)List all findings to the user before proceeding.
If the plugin's @backstage/* dependencies are outdated, upgrade them first using the backstage-upgrade skill (../backstage-upgrade/SKILL.md) before proceeding with migration.
NFS is not GA yet. Use the Alpha approach by default: NFS at ./alpha, legacy stays at root (.).
The Colocated approach is the alternative: NFS as default export in index.ts, legacy source in legacy.ts but re-exported from index.ts for backward compatibility. Use this when the user wants NFS and legacy APIs available from the same import path.
For each extension type found in Step 1, load the appropriate reference:
| Extension type | Reference to load |
|---|---|
| Pages, API factories | references/migrate-page.md |
| Entity content tabs or cards | references/migrate-entity-content.md |
| Translations / i18n | references/migrate-translations.md |
| RHDH drawers, header items, homepage widgets | references/migrate-rhdh-extensions.md |
| App-level wrappers or root elements | references/migrate-app-level.md |
Apply each reference's patterns to the discovered extensions. For page plugins, create NFS variants of page components without the page shell (dual header pattern in migrate-page.md).
Load references/package-json.md and apply the export configuration matching the chosen approach (alpha or colocated).
Load references/app-setup.md and:
dev/index.tsx (or dev/nfs.tsx) using createApp from @backstage/frontend-defaultsindex.ts maintain compatibility)Load references/verification.md and run all checks. Run yarn tsc from the workspace root (not just the plugin directory) to catch consumer import issues.
</migration_workflow>
<reference_index>
| Reference | Load when... |
|---|---|
references/migrate-page.md | Plugin has pages or API factories |
references/api-changes.md | Updating a plugin migrated against an older NFS version |
references/migrate-entity-content.md | Plugin has entity tabs or cards |
references/migrate-translations.md | Plugin has i18n/translations |
references/migrate-rhdh-extensions.md | Plugin uses RHDH drawer, header, or homepage widgets |
references/mount-point-mapping.md | Plugin uses RHDH dynamic plugin mount points (legacy config) |
references/migrate-app-level.md | Plugin has app-level wrappers or root elements |
references/package-json.md | Updating package.json exports |
references/app-setup.md | Setting up or updating the NFS dev app |
references/verification.md | Verifying the migration |
references/testing-rhdh.md | Testing with a real RHDH instance |
references/gotchas.md | Troubleshooting migration issues |
references/reference-prs.md | Looking for real migration examples |
references/operator-config.md | Plugin uses RHDH operator config or needs app.extensions / app.routes.bindings reference |
references/overview.md | User wants to learn about NFS before migrating |
references/support.md | User needs help beyond what the skill covers |
</reference_index>
<success_criteria>
./alpha (or root, for colocated) default-exports a createFrontendPlugin resulttitle and icon set (on PageBlueprint or createFrontendPlugin)package.json exports NFS at ./alpha (alpha approach) or . (colocated approach)createFrontendModule with pluginId: 'app'extensions array (or a catalog module if injecting from outside)yarn tsc and yarn build passindex.ts for colocated)</success_criteria>
npx claudepluginhub joshuarweaver/cascade-code-devops-misc-2 --plugin durandom-rhdh-skill3plugins reuse this skill
First indexed Jul 1, 2026
Migrate Backstage frontend plugins from the legacy system to the New Frontend System (NFS) using alpha exports and blueprints.
Guides gradual migration from legacy JavaScript/jQuery/MPA frontends to modern stacks (React, Vue, Next.js, Nuxt, TypeScript) while preserving behavior. Use for intentional tech stack upgrades, not routine bug fixes.
Builds a phased, reversible frontend migration plan using strangler-fig patterns with measurable exit criteria per phase. Works for legacy-to-modern migrations (jQuery/Backbone/AngularJS) and bundler upgrades (CRA/Webpack to Vite).