Backstage Software Templates logo

Backstage Backstage Software Templates Plugin

Created by Spotify

Backstage Software Templates is a core feature of Backstage that lets engineers create new components through a simple guided form. It pulls a code skeleton, fills in variables, then publishes to your source control. It can push to GitHub or GitLab. It can also run custom actions to fit your environment. In short, it gives you a paved path to start new software fast with your standards built in.

The Software Templates plugin adds a Create flow to your portal. Pick a template. Answer a few questions. The plugin scaffolds a repo, sets up CI, registers in the catalog, and can trigger docs or infra steps. Use it to spin up microservices, libraries, websites, data jobs, or internal tools. Platform teams encode guardrails in one place. Engineers get a clean starting point that matches company practice.

American Airlines showcases the feature in their Runway portal. “The quick and easy way to create applications with AA’s organizational best practices baked in.”

Installation Instructions

These instructions apply to self-hosted Backstage only. To use this plugin on Roadie, visit the docs.

Install the frontend package

Run these in the repo root

Copy
yarn workspace app add @backstage/plugin-scaffolder

Mount the Create page route

Edit packages/app/src/App.tsx

Copy
import React from 'react';
import { Route } from 'react-router-dom';
import { ScaffolderPage } from '@backstage/plugin-scaffolder';
import { FlatRoutes } from '@backstage/core-app-api';

// keep your existing imports above

export const App = () => (
  <AppProvider>
    <AppRouter>
      <Root>
        <FlatRoutes>
          {/* keep your existing routes */}
          <Route path="/create" element={<ScaffolderPage />} />
        </FlatRoutes>
      </Root>
    </AppRouter>
  </AppProvider>
);

Edit packages/app/src/components/Root/Root.tsx

Copy
import React from 'react';
import { SidebarPage, Sidebar, SidebarItem, SidebarSpace } from '@backstage/core-components';
import AddCircleOutline from '@mui/icons-material/AddCircleOutline';

// keep your existing imports

export const Root = ({ children }: { children?: React.ReactNode }) => (
  <SidebarPage>
    <Sidebar>
      {/* keep your existing sidebar items */}
      <SidebarItem icon={AddCircleOutline} to="/create" text="Create" />
      <SidebarSpace />
    </Sidebar>
    {children}
  </SidebarPage>
);

Install the backend on the new backend system

Add the backend package

Copy
yarn workspace backend add @backstage/plugin-scaffolder-backend

Register it in packages/backend/src/index.ts

Copy
import { createBackend } from '@backstage/backend-defaults';

const backend = createBackend();

// keep your other backend plugins
backend.add(import('@backstage/plugin-app-backend'));
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-catalog-backend'));

// add the scaffolder backend plugin
backend.add(import('@backstage/plugin-scaffolder-backend'));

backend.start();

You do not need to wire actions by hand in this setup. The plugin provides a standard set of actions.

Install the backend on the old backend system

Add the backend package

Copy
yarn workspace backend add @backstage/plugin-scaffolder-backend

Create packages/backend/src/plugins/scaffolder.ts

Copy
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import { CatalogClient } from '@backstage/catalog-client';
import { ScmIntegrations } from '@backstage/integration';
import {
  createRouter,
  createBuiltinActions,
} from '@backstage/plugin-scaffolder-backend';

export default async function createPlugin(
  env: PluginEnvironment,
): Promise<Router> {
  const catalogClient = new CatalogClient({ discoveryApi: env.discovery });
  const integrations = ScmIntegrations.fromConfig(env.config);

  const actions = createBuiltinActions({
    integrations,
    catalogClient,
    reader: env.reader,
    config: env.config,
  });

  return await createRouter({
    logger: env.logger,
    config: env.config,
    database: env.database,
    reader: env.reader,
    catalogClient,
    actions,
    identity: env.identity,
  });
}

Wire it into packages/backend/src/index.ts

Copy
// keep your existing imports
import scaffolder from './plugins/scaffolder';

// inside the main bootstrap function where you set up the routers
const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder'));
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));

Optional sample template for local testing

Place a template file at templates/example/template.yaml

Copy
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: example-template
  title: Example Template
  description: A minimal template that logs a value
spec:
  owner: [email protected]
  type: service
  parameters:
    - title: Fill in values
      properties:
        name:
          title: Name
          type: string
          description: A value to log
  steps:
    - id: show
      name: Show input
      action: debug:log
      input:
        message: 'Hello ${{ parameters.name }}'

Add a local file location in app-config.yaml so the catalog can load it

Copy
catalog:
  locations:
    - type: file
      target: templates/example/template.yaml
      rules:
        - allow: [Template]

Now the Create page can show this template.

Set up Backstage in minutes with Roadie