name: backstage-frontend-plugin description: "Build Backstage frontend plugins with the new Frontend System." version: 0.1.0
Backstage Frontend Plugin (New Frontend System)
About This Skill
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.
What This Skill Provides
- Specialized workflows - Step-by-step procedures for creating and configuring frontend plugins
- Best practices - Production-ready patterns for lazy loading, error handling, permissions, and testing
- Golden path templates - Copy/paste code snippets for common plugin patterns
- Domain expertise - Backstage-specific knowledge about blueprints, routes, and the plugin system
When to Use This Skill
Use this skill when creating UI features for Backstage: pages, navigation items, entity cards/content, or shared Utility APIs.
Development Workflow
Phase 1: Planning and Research
1.1 Understand the Requirements
Before building a frontend plugin, clearly understand:
- What UI features are needed (pages, navigation, entity content, cards)
- What data the plugin will display or interact with
- Whether Utility APIs are needed for shared logic
- Integration points with other plugins (catalog, permissions, etc.)
1.2 Load Reference Documentation
Load reference files as needed based on the plugin requirements:
For Extension Development:
- 📋 Extension Blueprints Reference - Comprehensive guide to PageBlueprint, EntityContentBlueprint, and ApiBlueprint
For Utility API Development:
- 🔌 Utility APIs Reference - Creating and using Utility APIs for shared logic
For Testing:
- ✅ Testing Reference - Comprehensive testing guide for components, extensions, and APIs
Phase 2: Implementation
Follow the Golden Path workflow below for implementation, referring to reference files as needed.
Phase 3: Testing
After implementing the plugin:
- Load the ✅ Testing Reference
- Write comprehensive tests for:
- React components using
renderInTestApp - Extensions using
createExtensionTester - Utility APIs with mocked dependencies
- React components using
- Run tests and achieve good coverage:
yarn backstage-cli package test --coverage
Phase 4: Review and Polish
Before publishing:
- Run linting and structure checks
- Ensure all extensions are properly registered
- Verify lazy loading and error boundaries
- Check permission-based visibility where appropriate
- Review the Common Pitfalls section below
Quick Facts
- Create a plugin with
yarn new→ select plugin; it generatesplugins/<pluginId>/. - The plugin instance is built with
createFrontendPluginfrom@backstage/frontend-plugin-api. Export it as the default fromsrc/index.ts. - Functionality is provided via extensions (e.g.,
PageBlueprint,EntityContentBlueprint). These are lazy‑loaded using dynamic imports. - Define route references with
createRouteRef(usually insrc/routes.ts) and use them in blueprints. - Use Utility APIs for shared logic (
createApiRef+ApiBlueprint), consumed viauseApi.
App integration patterns
- New Frontend System (preferred):
- Apps using
@backstage/frontend-defaultsdiscover plugin extensions when the plugin is included at app creation. - Add your plugin to the app’s feature/plugin list; no need to export components. Routes declared by
PageBlueprintare mounted automatically.
- Apps using
- Legacy apps (compat path):
- If the app is still using
@backstage/app-defaultsand manualFlatRoutes, add a<Route>that renders your page component directly. - It’s acceptable to export a page component temporarily to bridge legacy routing, but prefer keeping components internal once migrated.
- If the app is still using
Production Best Practices
Extensions and Routes
- Keep all
routeRefs insrc/routes.ts - Use
createSubRouteReffor nested paths - Export only the plugin (default from
src/index.ts) - Never export extensions/components from the package root
Lazy Loading and UX
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>
);
}
Utility APIs
- Keep API interfaces small and stable
- Wrap external clients/fetchers
- Provide a mock implementation for tests
- Register via
ApiBlueprintand consume withuseApi - Do not fetch in render—use hooks/effects
Permissions and Visibility
Hide/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>;
}
Entity Integrations
- Prefer small, focused entity content extensions
- Avoid heavy logic in loaders
- Keep presentational components separate from data hooks
Testing
- Use
@testing-library/reactto test extension output - Mock Utility APIs
- Verify routeRefs with
useRouteRefwhere navigation matters
Golden Path (Copy/Paste Workflow)
1) Scaffold
# 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=""
2) Utility API (optional)
// 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 },
});
3) Entity integration (optional)
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 />),
},
});
Verify in an app
- In a new Frontend System app:
- Ensure the app is created with
@backstage/frontend-defaultsand your plugin is included at app creation. - Start the repo and visit the path declared by your
PageBlueprint.
- Ensure the app is created with
Testing, linting & structure checks
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).
Common Pitfalls (and Fixes)
| 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 |
Reference Files
📚 Documentation Library
Load these resources as needed during development:
Extension Development
- 📋 Extension Blueprints Reference - Complete guide to all extension blueprints including:
- PageBlueprint for creating pages
- EntityContentBlueprint for entity tabs
- ApiBlueprint for Utility APIs
- Advanced features like
makeWithOverrides - Common patterns and best practices
Utility API Development
- 🔌 Utility APIs Reference - Creating and using Utility APIs including:
- API interface definition patterns
- Creating API references with
createApiRef - Implementing default API classes
- Registering with ApiBlueprint
- Using APIs in components with
useApi - Common API patterns (REST clients, caching, event bus)
- Testing strategies
Testing
- ✅ Testing Reference - Comprehensive testing guide including:
- Testing React components with
renderInTestApp - Testing extensions with
createExtensionTester - Mocking Utility APIs with
TestApiProvider - Testing permissions and entity components
- Routes and navigation testing
- Best practices and common patterns
- Testing React components with
External References
- Building Frontend Plugins (New Frontend System): createFrontendPlugin, blueprints, routes, Utility APIs. (Backstage)