About using-backstage-frontend-system
A skill for developers creating or editing Backstage plugins with the New Frontend System. It offers patterns for plugin structure, routes, APIs, and pages, including guidance for newly scaffolded plugins.
Key features
- 01Plugin and extension patterns
- 02Route reference guidance
- 03API exposure and data-fetch patterns
- 04Page and sub-page setup
- 05NFS hooks and API references
- 06Common implementation pitfalls
Use cases
- 01Defining a new NFS plugin
- 02Adding routes, pages, or sub-pages
- 03Exposing or reading plugin APIs
- 04Applying NFS patterns after scaffolding
Install
Copy the command and run it in a terminal.
$
npx skills add https:/ / github.com/ vinzscam/ backstage-skills --skill using-backstage-frontend-systemAfter installing, type/using-backstage-frontend-systemin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
references/api-extensions.mdhooks.mdpage-extensions.mdplugin-definition.mdroute-refs.mdsub-pages.mdDetails
From the project READMEBackstage New Frontend System (NFS) Patterns
When to use this skill
- Defining a new frontend plugin (
src/plugin.tsx) - Declaring route refs (
src/routes.ts) - Exposing APIs from a plugin (
src/apis.ts) - Adding pages, sub-pages, or other extensions (
src/extensions.tsx) - Using NFS hooks (
useApi,useRouteRef)
Choose the right pattern
| Building... | Pattern | Reference |
|---|---|---|
| The plugin itself | createFrontendPlugin({ pluginId, ..., extensions }) | references/plugin-definition.md |
| Route refs | createRouteRef, createSubRouteRef, createExternalRouteRef from @backstage/frontend-plugin-api | references/route-refs.md |
| Calling a backend endpoint (default) | A data hook wrapping useAsync / useAsyncFn around fetchApi.fetch — no client class, no extension | references/api-extensions.md |
| Exposing an extensible API on an open-source plugin | ApiBlueprint.make({ params }) | references/api-extensions.md |
| A page (single route) | PageBlueprint.make({ params: { path, routeRef, loader } }) | references/page-extensions.md |
| A page with tabs | PageBlueprint (no loader) + SubPageBlueprints | references/sub-pages.md |
| Reading APIs in components | useApi(apiRef), useRouteRef(routeRef) from @backstage/frontend-plugin-api | references/hooks.md |
Two invocation modes
- Standalone: user is editing an existing NFS plugin. Pick a reference and apply.
- Post-scaffold (called by
creating-backstage-pluginforfrontend-plugin/frontend-plugin-moduletemplates): wire up the canonical NFS structure in the freshly scaffolded plugin —src/routes.ts,src/plugin.tsxusingcreateFrontendPlugin, and an examplePageBlueprintextension.
Common gotchas
- In NFS, plugin-system imports come from
@backstage/frontend-plugin-api, not@backstage/core-plugin-api. Hooks (useApi,useRouteRef), refs (identityApiRef,configApiRef,discoveryApiRef,fetchApiRef,storageApiRef,analyticsApiRef), and primitives (createApiRef) all live in the new package. createRouteRef()takes noidargument — the id is derived from the extension that owns the route.createExternalRouteRef({ defaultTarget: '<pluginId>.<routeName>' })— setdefaultTargetto the most common binding so apps don't need explicitbindRoutescalls.useRouteRef(externalRef)may returnundefinedif the external route isn't bound — always handle the undefined case.- Page components inside a
PageBlueprintloader should NOT includePage,Header, orPageWithHeaderfrom@backstage/core-components. The framework'sPageLayoutrendersPluginHeaderautomatically. UseContainerfrom@backstage/uias the outermost wrapper for page content. UseHeaderfrom@backstage/uionly when you need a subtitle or custom actions. SubPageBlueprintpaths are relative (no leading/). Sub-page components render content only — noPage, noHeader, noHeaderTabs. Wrap sub-page content inContainerfrom@backstage/uiif padding is needed.createApiRef<T>().with({ id, pluginId })— the builder form is preferred so ownership is explicit. Theidmust still be globally unique. Only the owning plugin (or a module targeting it) can override the API.- For any
fetchApi.fetchcall, handle non-OK responses withthrow await ResponseError.fromResponse(response)from@backstage/errors— it parses the response body for structured error info and produces a typed error other Backstage UI surfaces (e.g.<ResponseErrorPanel>) understand. Never justthrow new Error(\Failed: ${response.status}`)`.
Comments
Markdown supported