ワンクリックで
backstage-frontend-plugin
Build Backstage frontend plugins with the new Frontend System.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Build Backstage frontend plugins with the new Frontend System.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | backstage-frontend-plugin |
| description | Build Backstage frontend plugins with the new Frontend System. |
| version | 0.1.0 |
This skill provides specialized knowledge and workflows for building Backstage frontend plugins using the New Frontend System. It guides the development of UI features including pages, navigation items, entity cards/content, and shared Utility APIs.
Use this skill when creating UI features for Backstage: pages, navigation items, entity cards/content, or shared Utility APIs.
Before building a frontend plugin, clearly understand:
Load reference files as needed based on the plugin requirements:
For Extension Development:
For Utility API Development:
For Testing:
Follow the Golden Path workflow below for implementation, referring to reference files as needed.
After implementing the plugin:
renderInTestAppcreateExtensionTesteryarn backstage-cli package test --coverage
Before publishing:
yarn new → select plugin; it generates plugins/<pluginId>/.createFrontendPlugin from @backstage/frontend-plugin-api. Export it as the default from src/index.ts.PageBlueprint, EntityContentBlueprint). These are lazy‑loaded using dynamic imports.createRouteRef (usually in src/routes.ts) and use them in blueprints.createApiRef + ApiBlueprint), consumed via useApi.@backstage/frontend-defaults discover plugin extensions when the plugin is included at app creation.PageBlueprint are mounted automatically.@backstage/app-defaults and manual FlatRoutes, add a <Route> that renders your page component directly.routeRefs in src/routes.tscreateSubRouteRef for nested pathssrc/index.ts)Use dynamic imports in loaders and wrap rendered elements in Suspense with a lightweight fallback. Add an error boundary for resilience:
// Suspense and error boundary around a lazy extension element
const Example = React.lazy(() => import('./components/ExamplePage'));
function ExampleWrapper() {
return (
<ErrorBoundary>
<React.Suspense fallback={<div>Loading…</div>}>
<Example />
</React.Suspense>
</ErrorBoundary>
);
}
ApiBlueprint and consume with useApiHide/show entity content based on permissions or ownership to avoid broken UX for unauthorized users:
import { usePermission } from '@backstage/plugin-permission-react';
import { somePermission } from '@backstage/plugin-permission-common';
export function ExampleEntityContent() {
const { loading, allowed } = usePermission({ permission: somePermission });
if (loading) return null;
if (!allowed) return null; // or render a friendly message/banner
return <div>Secret content</div>;
}
@testing-library/react to test extension outputuseRouteRef where navigation matters# From the repository root (interactive)
yarn new
# Select: frontend-plugin
# Enter plugin id (kebab case, e.g. example)
# Non-interactive (for AI agents/automation)
yarn new --select frontend-plugin --option pluginId=example --option owner=""
// src/api.ts
import { createApiRef } from '@backstage/frontend-plugin-api';
export interface ExampleApi {
getExample(): { example: string };
}
export const exampleApiRef = createApiRef<ExampleApi>({ id: 'plugin.example' });
export class DefaultExampleApi implements ExampleApi {
getExample() {
return { example: 'Hello World!' };
}
}
Register it with the ApiBlueprint and consume via useApi:
// src/plugin.ts
import { ApiBlueprint } from '@backstage/frontend-plugin-api';
import { exampleApiRef, DefaultExampleApi } from './api';
const exampleApi = ApiBlueprint.make({
name: 'example',
params: define =>
define({
api: exampleApiRef,
deps: {},
factory: () => new DefaultExampleApi(),
}),
});
export const examplePlugin = createFrontendPlugin({
pluginId: 'example',
extensions: [exampleApi, examplePage, exampleNavItem],
routes: { root: rootRouteRef },
});
import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
const exampleEntityContent = EntityContentBlueprint.make({
params: {
path: 'example',
title: 'Example',
loader: () =>
import('./components/ExampleEntityContent').then(m => <m.ExampleEntityContent />),
},
});
@backstage/frontend-defaults and your plugin is included at app creation.PageBlueprint.Run tests and lints with Backstage's CLI:
yarn backstage-cli package test
yarn backstage-cli package lint
yarn backstage-cli repo lint
Keep a predictable structure (API layer, hooks, components, routes.ts, plugin.ts, index.ts).
| Problem | Solution | Reference |
|---|---|---|
| Extensions don't render | Ensure they're passed in the plugin's extensions array; components must be lazy-loaded via dynamic imports | Backstage |
| Navigation/links break | Keep routeRefs in src/routes.ts and use useRouteRef to generate links | Backstage |
| Consumers can't install your plugin | Export the plugin as the default from src/index.ts | Backstage |
Load these resources as needed during development:
makeWithOverridescreateApiRefuseApirenderInTestAppcreateExtensionTesterTestApiProvider