$npx -y skills add redhat-developer/rhdh-skill --skill nfs-migrationMigrate Backstage frontend plugins from the legacy system to the New Frontend System (NFS). Use when asked to "migrate to NFS", "new frontend system", "convert plugin to NFS", "createFrontendPlugin", "PageBlueprint", "ApiBlueprint", "SubPageBlueprint", "alpha to GA", "legacy to N
| 1 | <essential_principles> |
| 2 | |
| 3 | <principle name="discover_first"> |
| 4 | 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. |
| 5 | </principle> |
| 6 | |
| 7 | <principle name="nfs_at_alpha"> |
| 8 | 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. |
| 9 | </principle> |
| 10 | |
| 11 | <principle name="upstream_apis"> |
| 12 | 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. |
| 13 | </principle> |
| 14 | |
| 15 | <principle name="modules_not_plugins"> |
| 16 | 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. |
| 17 | </principle> |
| 18 | |
| 19 | <principle name="shared_components"> |
| 20 | 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. |
| 21 | </principle> |
| 22 | |
| 23 | <principle name="keep_legacy"> |
| 24 | 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. |
| 25 | </principle> |
| 26 | |
| 27 | </essential_principles> |
| 28 | |
| 29 | <intake> |
| 30 | |
| 31 | ## What would you like to do? |
| 32 | |
| 33 | 1. **Migrate a plugin to NFS** — Analyze your existing plugin and convert it to the New Frontend System |
| 34 | 2. **Test a migrated plugin in RHDH** — Deploy and verify in a local or cluster RHDH instance |
| 35 | 3. **Learn about NFS migration** — Read the migration guide |
| 36 | |
| 37 | **Wait for response before proceeding.** |
| 38 | |
| 39 | </intake> |
| 40 | |
| 41 | <routing> |
| 42 | |
| 43 | | Response | Action | |
| 44 | |----------|--------| |
| 45 | | 1, "migrate", "convert", "NFS" | Follow the migration workflow below | |
| 46 | | 2, "test", "verify", "deploy" | Read `workflows/test-nfs-plugin.md` | |
| 47 | | 3, "learn", "guide", "overview" | Read `references/overview.md` and present key sections to the user | |
| 48 | |
| 49 | </routing> |
| 50 | |
| 51 | <migration_workflow> |
| 52 | |
| 53 | ### Step 1: Discover |
| 54 | |
| 55 | Read `package.json` and `src/plugin.ts` (or `src/plugin.tsx`). Identify: |
| 56 | - Plugin ID |
| 57 | - Routes and route refs |
| 58 | - API factories |
| 59 | - Routable extensions (pages) |
| 60 | - Component extensions (entity cards, tabs) |
| 61 | - Sidebar/nav items |
| 62 | - Translations |
| 63 | - RHDH-specific extensions (drawers, header items, homepage widgets) |
| 64 | - RHDH dynamic plugin mount points (`app-config.dynamic.yaml` — see `references/mount-point-mapping.md`) |
| 65 | |
| 66 | List all findings to the user before proceeding. |
| 67 | |
| 68 | If the plugin's `@backstage/*` dependencies are outdated, upgrade them first using the `backstage-upgrade` skill (`../backstage-upgrade/SKILL.md`) before proceeding with migration. |
| 69 | |
| 70 | ### Step 2: Choose Approach |
| 71 | |
| 72 | NFS is not GA yet. Use the **Alpha** approach by default: NFS at `./alpha`, legacy stays at root (`.`). |
| 73 | |
| 74 | 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. |
| 75 | |
| 76 | ### Step 3: Migrate Extensions |
| 77 | |
| 78 | For each extension type found in Step 1, load the appropriate reference: |
| 79 | |
| 80 | | Extension type | Reference to load | |
| 81 | |----------------|-------------------| |
| 82 | | Pages, API factories | `references/migrate-page.md` | |
| 83 | | Entity content tabs or cards | `references/migrate-entity-content.md` | |
| 84 | | Translations / i18n | `references/migrate-translations.md` | |
| 85 | | RHDH drawers, header items, homepage widgets | `references/migrate-rhdh-extensions.md` | |
| 86 | | App-level wrappers or root elements | `references/migrate-app-level.md` | |
| 87 | |
| 88 | 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`). |
| 89 | |
| 90 | ### Step 4: Update package.json |
| 91 | |
| 92 | Load `references/package-json.md |