or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

components.mddev-app.mdindex.md
tile.json

dev-app.mddocs/

Development App Framework

Complete framework for building standalone development applications to test Backstage plugins in isolation, with built-in authentication, theming, routing, and internationalization support.

Capabilities

createDevApp Function

Factory function that creates a new DevAppBuilder instance for configuring and building development applications.

/**
 * Creates a dev app builder for rendering plugins in development
 * @returns DevAppBuilder instance for method chaining
 */
function createDevApp(): DevAppBuilder;

Usage Examples:

import { createDevApp } from "@backstage/dev-utils";

// Create and configure a development app
const DevApp = createDevApp()
  .registerPlugin(myPlugin)
  .addPage({
    element: <MyPluginPage />,
    title: "My Plugin",
    path: "/my-plugin"
  })
  .build();

isReactRouterBeta Function

Utility function that detects whether React Router is in beta version by testing the behavior of route creation.

/**
 * Detects if React Router is in beta version
 * @returns true if React Router beta is detected, false otherwise
 */
function isReactRouterBeta(): boolean;

Usage Examples:

import { isReactRouterBeta } from "@backstage/dev-utils";

// Check React Router version for conditional behavior
if (isReactRouterBeta()) {
  console.log("Using React Router beta version");
  // Apply beta-specific configuration
} else {
  console.log("Using stable React Router version");
  // Apply stable version configuration
}

// Useful for conditional imports or feature detection
const routerConfig = isReactRouterBeta() 
  ? betaRouterConfig 
  : stableRouterConfig;

The function works by creating a test route with an index element and checking if the resulting route object has the index property, which behaves differently between React Router versions.

DevAppBuilder Class

Builder class that provides a fluent API for configuring development applications with plugins, pages, themes, and authentication.

/**
 * Builder class for creating development applications
 * Provides fluent API for configuration before building the final component
 */
class DevAppBuilder {
  /** Register one or more plugins to render in the dev app */
  registerPlugin(...plugins: BackstagePlugin[]): DevAppBuilder;
  
  /** Register an API factory to add to the app */
  registerApi<Api, Impl extends Api, Deps extends { [name in string]: unknown }>(
    factory: ApiFactory<Api, Impl, Deps>
  ): DevAppBuilder;
  
  /** Add a React node to place just inside the App Provider */
  addRootChild(node: ReactNode): DevAppBuilder;
  
  /** Add a new sidebar item without a corresponding page */
  addSidebarItem(sidebarItem: JSX.Element): DevAppBuilder;
  
  /** Add a page component along with accompanying sidebar item */
  addPage(opts: DevAppPageOptions): DevAppBuilder;
  
  /** Add an array of themes to override the default theme */
  addThemes(themes: AppTheme[]): DevAppBuilder;
  
  /** Add new sign in provider for the dev app */
  addSignInProvider(provider: SignInProviderConfig): DevAppBuilder;
  
  /** Set available languages to be shown in the dev app */
  setAvailableLanguages(languages: string[]): DevAppBuilder;
  
  /** Add translation resource to the dev app */
  addTranslationResource(resource: TranslationResource): DevAppBuilder;
  
  /** Set default language for the dev app */
  setDefaultLanguage(language: string): DevAppBuilder;
  
  /** Build a DevApp component using the resources registered so far */
  build(): ComponentType<PropsWithChildren<{}>>;
  
  /** Build and render directly to #root element, with react hot loading */
  render(): void;
}

Usage Examples:

import { createDevApp } from "@backstage/dev-utils";
import { myPlugin, anotherPlugin } from "./plugins";
import { myCustomApi } from "./apis";

// Full configuration example
const devApp = createDevApp()
  // Register plugins
  .registerPlugin(myPlugin, anotherPlugin)
  
  // Register custom APIs
  .registerApi(myCustomApi)
  
  // Add pages with sidebar navigation
  .addPage({
    element: <MyPluginPage />,
    title: "My Plugin",
    path: "/my-plugin",
    icon: MyIcon
  })
  .addPage({
    element: <AnotherPage />,
    title: "Another Page", 
    path: "/another"
  })
  
  // Add standalone sidebar items
  .addSidebarItem(
    <SidebarItem to="/external" text="External Link" icon={LinkIcon} />
  )
  
  // Configure internationalization
  .setAvailableLanguages(['en', 'es', 'fr'])
  .setDefaultLanguage('en')
  .addTranslationResource(myTranslations)
  
  // Add custom themes
  .addThemes([lightTheme, darkTheme])
  
  // Add authentication providers
  .addSignInProvider({
    id: 'github',
    title: 'GitHub',
    message: 'Sign in with GitHub'
  })
  
  // Add global components
  .addRootChild(<GlobalErrorBoundary key="error-boundary" />);

// Build and render
devApp.render();

// Or build for manual rendering
const DevAppComponent = devApp.build();
ReactDOM.render(<DevAppComponent />, document.getElementById('root'));

DevAppPageOptions Interface

Configuration options for adding pages to the development app.

/**
 * Configuration options for dev app pages
 */
interface DevAppPageOptions {
  /** URL path for the page. Auto-generated if not provided */
  path?: string;
  
  /** React element to render for this page */
  element: JSX.Element;
  
  /** Optional child components for nested routing */
  children?: JSX.Element;
  
  /** Page title for sidebar item. No sidebar item created if not provided */
  title?: string;
  
  /** Icon for sidebar item. Defaults to BookmarkIcon if title is provided */
  icon?: IconComponent;
}

Usage Examples:

// Page with auto-generated path and default icon
devApp.addPage({
  element: <MyComponent />,
  title: "My Page"  // Creates sidebar item with BookmarkIcon
});

// Page with custom path and icon
devApp.addPage({
  element: <DashboardPage />,
  title: "Dashboard", 
  path: "/dashboard",
  icon: DashboardIcon
});

// Page without sidebar item (no title)
devApp.addPage({
  element: <HiddenPage />,
  path: "/hidden"  // No sidebar item created
});

// Page with nested routing
devApp.addPage({
  element: <ParentPage />,
  children: <ChildRoutes />,
  title: "Parent Page",
  path: "/parent/*"
});

Automatic Features

The DevAppBuilder automatically provides:

Built-in Authentication

  • Guest authentication enabled by default
  • Support for additional sign-in providers via addSignInProvider
  • Automatic sign-out functionality in sidebar

Default Sidebar Items

  • Theme switcher for switching between light/dark themes
  • Language switcher (when multiple languages available)
  • Sign-out button

Routing & Navigation

  • React Router integration with AppRouter and FlatRoutes
  • Automatic route generation for pages
  • Sidebar navigation with active state management
  • External route binding for plugin inter-dependencies

API Integration

  • Default SCM integrations API
  • Support for custom API factories
  • Automatic API dependency resolution

Error Handling

  • AlertDisplay for global error notifications
  • OAuthRequestDialog for authentication flows
  • Error boundaries and fallback UI

Advanced Configuration

Custom API Registration

import { createApiFactory, configApiRef } from "@backstage/core-plugin-api";

const customApiFactory = createApiFactory({
  api: myApiRef,
  deps: { configApi: configApiRef },
  factory: ({ configApi }) => new MyApiImpl(configApi)
});

devApp.registerApi(customApiFactory);

Theme Customization

import { createUnifiedTheme } from "@backstage/theme";

const myCustomTheme = createUnifiedTheme({
  palette: {
    primary: { main: '#1976d2' },
    secondary: { main: '#dc004e' }
  }
});

devApp.addThemes([myCustomTheme]);

Translation Resources

const spanishTranslations = {
  'en': { 'welcome': 'Welcome' },
  'es': { 'welcome': 'Bienvenido' }
};

devApp
  .setAvailableLanguages(['en', 'es'])
  .setDefaultLanguage('en')
  .addTranslationResource(spanishTranslations);

Required Dependencies

The development app framework requires these peer dependencies:

{
  "@backstage/app-defaults": "workspace:^",
  "@backstage/core-app-api": "workspace:^",
  "@backstage/core-components": "workspace:^",
  "@backstage/core-plugin-api": "workspace:^",
  "@backstage/integration-react": "workspace:^",
  "@material-ui/core": "^4.12.2",
  "react": "^17.0.0 || ^18.0.0",
  "react-dom": "^17.0.0 || ^18.0.0",
  "react-router-dom": "^6.3.0"
}

Types

import { 
  BackstagePlugin,
  ApiFactory,
  AnyApiFactory,
  AppTheme,
  SignInProviderConfig,
  IconComponent
} from '@backstage/core-plugin-api';
import { TranslationResource } from '@backstage/core-plugin-api/alpha';
import { ComponentType, PropsWithChildren, ReactNode } from 'react';
import { DevAppBuilder, DevAppPageOptions } from '@backstage/dev-utils';

// Backstage plugin definition
interface BackstagePlugin {
  getId(): string;
  provide<T>(extension: T): T;
  routes: Record<string, any>;
  externalRoutes: Record<string, any>;
}

// API factory for dependency injection
interface ApiFactory<Api, Impl extends Api, Deps extends { [name in string]: unknown }> {
  api: { id: string };
  deps: Deps;
  factory(deps: Deps): Impl;
}

// Any API factory type
type AnyApiFactory = ApiFactory<any, any, any>;

// Application theme definition
interface AppTheme {
  id: string;
  title: string;
  variant: 'light' | 'dark';
  icon?: IconComponent;
  Provider: ComponentType<PropsWithChildren<{}>>;
}

// Sign-in provider configuration
interface SignInProviderConfig {
  id: string;
  title: string;
  message: string;
  apiRef?: any;
}

// Translation resource for i18n
interface TranslationResource {
  [languageCode: string]: {
    [messageKey: string]: string;
  };
}

// Development app builder class (exported as type)
class DevAppBuilder {
  registerPlugin(...plugins: BackstagePlugin[]): DevAppBuilder;
  registerApi<Api, Impl extends Api, Deps extends { [name in string]: unknown }>(
    factory: ApiFactory<Api, Impl, Deps>
  ): DevAppBuilder;
  addRootChild(node: ReactNode): DevAppBuilder;
  addSidebarItem(sidebarItem: JSX.Element): DevAppBuilder;
  addPage(opts: DevAppPageOptions): DevAppBuilder;
  addThemes(themes: AppTheme[]): DevAppBuilder;
  addSignInProvider(provider: SignInProviderConfig): DevAppBuilder;
  setAvailableLanguages(languages: string[]): DevAppBuilder;
  addTranslationResource(resource: TranslationResource): DevAppBuilder;
  setDefaultLanguage(language: string): DevAppBuilder;
  build(): ComponentType<PropsWithChildren<{}>>;
  render(): void;
}

// Configuration options for dev app pages (exported as type)
interface DevAppPageOptions {
  path?: string;
  element: JSX.Element;
  children?: JSX.Element;
  title?: string;
  icon?: IconComponent;
}

// React component types
type ComponentType<P = {}> = React.ComponentType<P>;
type PropsWithChildren<P = {}> = P & { children?: ReactNode };
type ReactNode = React.ReactNode;