mcpField
U
using-backstage-frontend-system

using-backstage-frontend-system

github.com/vinzscamDevelopment446 installsUpdated 9w ago
Repository

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

  1. 01Plugin and extension patterns
  2. 02Route reference guidance
  3. 03API exposure and data-fetch patterns
  4. 04Page and sub-page setup
  5. 05NFS hooks and API references
  6. 06Common implementation pitfalls

Use cases

  1. 01Defining a new NFS plugin
  2. 02Adding routes, pages, or sub-pages
  3. 03Exposing or reading plugin APIs
  4. 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-system

After 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.md
hooks.md
page-extensions.md
plugin-definition.md
route-refs.md
sub-pages.md

Details

From the project README

Backstage 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...PatternReference
The plugin itselfcreateFrontendPlugin({ pluginId, ..., extensions })references/plugin-definition.md
Route refscreateRouteRef, createSubRouteRef, createExternalRouteRef from @backstage/frontend-plugin-apireferences/route-refs.md
Calling a backend endpoint (default)A data hook wrapping useAsync / useAsyncFn around fetchApi.fetch — no client class, no extensionreferences/api-extensions.md
Exposing an extensible API on an open-source pluginApiBlueprint.make({ params })references/api-extensions.md
A page (single route)PageBlueprint.make({ params: { path, routeRef, loader } })references/page-extensions.md
A page with tabsPageBlueprint (no loader) + SubPageBlueprintsreferences/sub-pages.md
Reading APIs in componentsuseApi(apiRef), useRouteRef(routeRef) from @backstage/frontend-plugin-apireferences/hooks.md

Two invocation modes

  • Standalone: user is editing an existing NFS plugin. Pick a reference and apply.
  • Post-scaffold (called by creating-backstage-plugin for frontend-plugin / frontend-plugin-module templates): wire up the canonical NFS structure in the freshly scaffolded plugin — src/routes.ts, src/plugin.tsx using createFrontendPlugin, and an example PageBlueprint extension.

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 no id argument — the id is derived from the extension that owns the route.
  • createExternalRouteRef({ defaultTarget: '<pluginId>.<routeName>' }) — set defaultTarget to the most common binding so apps don't need explicit bindRoutes calls.
  • useRouteRef(externalRef) may return undefined if the external route isn't bound — always handle the undefined case.
  • Page components inside a PageBlueprint loader should NOT include Page, Header, or PageWithHeader from @backstage/core-components. The framework's PageLayout renders PluginHeader automatically. Use Container from @backstage/ui as the outermost wrapper for page content. Use Header from @backstage/ui only when you need a subtitle or custom actions.
  • SubPageBlueprint paths are relative (no leading /). Sub-page components render content only — no Page, no Header, no HeaderTabs. Wrap sub-page content in Container from @backstage/ui if padding is needed.
  • createApiRef<T>().with({ id, pluginId }) — the builder form is preferred so ownership is explicit. The id must still be globally unique. Only the owning plugin (or a module targeting it) can override the API.
  • For any fetchApi.fetch call, handle non-OK responses with throw 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 just throw new Error(\Failed: ${response.status}`)`.

Comments

Markdown supported