mcpField
C
creating-backstage-entity-page-extension

creating-backstage-entity-page-extension

github.com/vinzscamDevelopment445 installsUpdated 9w ago
Repository

About creating-backstage-entity-page-extension

A development skill for extending Backstage catalog entity pages with cards or dedicated tabs. It covers blueprint selection, entity-based filtering, and implementation details for standalone or scaffolded plugins.

Key features

  1. 01Add cards to entity overview pages
  2. 02Create entity tabs with their own routes
  3. 03Filter extensions by entity properties
  4. 04Load components asynchronously for code-splitting
  5. 05Use entity context for data fetching

Use cases

  1. 01Displaying Jira project details on catalog entities
  2. 02Adding an incidents tab to a component page
  3. 03Restricting an extension to selected entity types

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/vinzscam/backstage-skills --skill creating-backstage-entity-page-extension

After installing, type/creating-backstage-entity-page-extensionin Claude Code to confirm it loaded.

Bundled filesreferences/card.mdreferences/content.md

Details

From the project README

Creating Catalog Entity Page Extensions

When to use this skill

  • Adding a card to the Overview tab of a catalog entity (e.g. "show Jira project info on Components linked to a Jira project")
  • Adding a whole tab (a content panel with its own URL path) to a catalog entity page (e.g. "/incidents" tab on Components, listing the component's incidents)
  • Filtering when an extension appears, based on entity kind, spec type, annotation presence, or any custom predicate

This is the most common reason to scaffold a frontend-plugin-module. The plugin you're authoring extends the host's catalog plugin via these blueprints.

Choose the right pattern

Building...PatternReference
A card on the entity Overview tabEntityCardBlueprint.make({ name, params: { loader, filter?, type? } })references/card.md
A full tab (its own URL path) on the entity pageEntityContentBlueprint.make({ name, params: { path, title, loader, filter?, group?, icon? } })references/content.md

Both blueprints come from @backstage/plugin-catalog-react/alpha. They share:

  • A loader: () => Promise<JSX.Element> for code-splitting the rendered component.
  • A filter parameter that controls when the extension appears (see "Entity filtering" below).
  • The component renders inside the catalog entity context — read the current entity via useEntity() from @backstage/plugin-catalog-react.

Two invocation modes

  • Standalone: editing an existing frontend-plugin-module that targets the catalog. Pick the right reference (card or content) and add the extension.
  • Post-scaffold (called by creating-backstage-plugin for frontend-plugin-module templates targeting catalog): scaffold the extension(s) the user described, plus the component(s) they render.

Entity filtering

The filter parameter on both blueprints decides which entities the extension applies to. Three accepted forms:

  1. String (most common): 'kind:component', 'kind:component,spec.type:service', 'metadata.annotations.acme.com/jira-project'. Comma-separated for AND, multiple forms for the same key are OR.
  2. FilterPredicate (structured): a Zod-validated object form for richer logic. Verify exact shape against ../backstage/packages/filter-predicates/src/.
  3. Function: (entity: Entity) => boolean — full programmatic control. Use sparingly — string filters compose better with the upstream config-override system.

If filter is omitted, the extension appears for all entity kinds. Almost always wrong — set a filter even if it's just 'kind:component'.

Common gotchas

  • The blueprints come from @backstage/plugin-catalog-react/alpha (currently @alpha). Track the import path against ../backstage/plugins/catalog-react/src/alpha/blueprints/.
  • The component itself imports useEntity from @backstage/plugin-catalog-react (main entry, not /alpha). The hook returns { entity } typed as Entity — use the catalog entity model from @backstage/catalog-model for typed access to metadata, spec, etc.
  • useEntity only works inside a component rendered by one of these blueprints — calling it outside the catalog entity context throws.
  • The loader: () => Promise<JSX.Element> form is required for code-splitting. Don't pass a synchronous component import — use () => import('./MyCard').then(m => <m.MyCard />).
  • For entity-specific data fetching, combine useEntity with the data-hook convention from using-backstage-frontend-system (a hook wrapping useAsync around fetchApi.fetch). Pass entity.metadata.name or an annotation as the cache key.
  • The host app's catalog plugin owns the route entity-content:catalog/overview (where cards attach) and page:catalog/entity (where content tabs attach). Your module installs into the same backend as the catalog plugin — adopters install via backend.add(...) and app.installPlugin(...); nothing else is needed.

Comments

Markdown supported