Tech Radar logo

Backstage Tech Radar Plugin

Created by Spotify

Tech Radar maps your technology choices in one view. It groups items into quadrants like languages, frameworks, infrastructure, and processes. Rings show maturity such as Adopt, Trial, Assess, and Hold. The point is shared guidance with a clear status for each tool. It also captures how things move over time.

The Tech Radar plugin brings this model into Backstage. It renders an interactive radar inside your portal. Engineers can search, filter, open links, and read context. Entries can include descriptions and a timeline of changes. You can run one radar or many. Teams use it to align standards, plan migrations, and highlight what to avoid.

Companies use this plugin today. Spotify says it “provides a visual and concise summary of technologies in use at your organization.”.

If you want a single place to see tech decisions and their status, this plugin gives you that view inside Backstage. It makes choices visible. It reduces thrash. It helps teams move with confidence.

A screenshot of the Tech Radar plugin.

Installation Instructions

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

Install the frontend package

  1. Add the plugin to the app package
Copy
yarn --cwd packages/app add @backstage-community/plugin-tech-radar
  1. Register the page in App.tsx so you can visit it at the tech radar path
Copy
// packages/app/src/App.tsx
import React from 'react';
import { FlatRoutes } from '@backstage/core-app-api';
import { Route } from 'react-router';
import { TechRadarPage } from '@backstage-community/plugin-tech-radar';

export const AppRoutes = () => (
  <FlatRoutes>
    {/* other routes */}
    <Route path="/tech-radar" element={<TechRadarPage />} />
  </FlatRoutes>
);
  1. Optional configuration on the page if you want custom size or titles
Copy
<Route
  path="/tech-radar"
  element={
    <TechRadarPage
      title="Tech Radar"
      subtitle="Engineering guidance"
      pageTitle="Tech Radar"
      width={1400}
      height={800}
    />
  }
/>
  1. Optional use of the component on your own page
Copy
// packages/app/src/components/TechRadar/CustomRadarPage.tsx
import React from 'react';
import { Content, Page } from '@backstage/core-components';
import { TechRadarComponent } from '@backstage-community/plugin-tech-radar';

export const CustomRadarPage = () => (
  <Page themeId="tool">
    <Content>
      <TechRadarComponent width={1200} height={800} />
    </Content>
  </Page>
);

Install the backend package

  1. Add the backend plugin
Copy
yarn --cwd packages/backend add @backstage-community/plugin-tech-radar-backend

New backend system

  1. Register the backend feature
Copy
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';

const backend = createBackend();

// other feature additions
backend.add(import('@backstage-community/plugin-tech-radar-backend'));

backend.start();

Legacy backend system

  1. Create the router
Copy
// packages/backend/src/plugins/techRadar.ts
import { createRouter } from '@backstage-community/plugin-tech-radar-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';

export default async function createPlugin(
  env: PluginEnvironment,
): Promise<Router> {
  return await createRouter({
    logger: env.logger,
    config: env.config,
    reader: env.reader,
  });
}
  1. Wire the router into the backend
Copy
// packages/backend/src/index.ts
import techRadar from './plugins/techRadar';

// inside main where other envs are created
const techRadarEnv = useHotMemoize(module, () => createEnv('techRadar'));

// inside apiRouter wiring with other routers
apiRouter.use('/tech-radar', await techRadar(techRadarEnv));

Configure the data source

  1. Point the backend to a JSON file that follows the TechRadarLoaderResponse schema
Copy
# app-config.yaml
techRadar:
  url: https://github.com/backstage/community-plugins/blob/main/workspaces/tech-radar/plugins/tech-radar-common/src/sampleTechRadarResponse.json
  1. Use your own file if needed
Copy
# app-config.yaml
techRadar:
  url: https://raw.githubusercontent.com/your-org/your-repo/main/radar.json

If your file is in a private repo, set up the matching integration in app config so the url reader can authenticate.

Optional custom data loading on the frontend

If you want to bypass the backend and load data yourself, implement TechRadarApi and register it in apis.ts. This takes precedence over the default client.

  1. Create a client
Copy
// packages/app/src/lib/MyTechRadarClient.ts
import { TechRadarApi, techRadarApiRef } from '@backstage-community/plugin-tech-radar';
import { TechRadarLoaderResponse } from '@backstage-community/plugin-tech-radar-common';

export class MyTechRadarClient implements TechRadarApi {
  async load(id: string | undefined): Promise<TechRadarLoaderResponse> {
    const res = await fetch('https://example.com/radar.json');
    const data = await res.json();

    return {
      ...data,
      entries: data.entries.map((entry: any) => ({
        ...entry,
        timeline: entry.timeline.map((t: any) => ({
          ...t,
          date: new Date(t.date),
        })),
      })),
    };
  }
}
  1. Register the client
Copy
// packages/app/src/apis.ts
import { AnyApiFactory, createApiFactory } from '@backstage/core-plugin-api';
import { techRadarApiRef } from '@backstage-community/plugin-tech-radar';
import { MyTechRadarClient } from './lib/MyTechRadarClient';

export const apis: AnyApiFactory[] = [
  createApiFactory(techRadarApiRef, new MyTechRadarClient()),
];

Data model reference

If you maintain your own JSON, match these shapes

Copy
// quadrants clockwise from bottom right
type Quadrant = { id: string; name: string };

// rings from inner to outer
type Ring = { id: string; name: string; color: string; description?: string };

type Timeline = {
  moved: -1 | 0 | 1;
  ringId: string;
  date: string; // YYYY-MM-dd
  description: string;
};

type Entry = {
  id: string;
  key: string;
  title: string;
  description?: string;
  quadrant: string; // must match a quadrant id
  url?: string; // deprecated, prefer links
  links?: Array<{ title: string; url: string }>;
  timeline: Timeline[];
};

type TechRadarLoaderResponse = {
  title?: string;
  quadrants: Quadrant[];
  rings: Ring[];
  entries: Entry[];
};

Place your JSON where your url reader can reach it, then set the url in app config.

Things to Know

  • The frontend will use the backend by default. If you register a custom TechRadarApi, your client will be used instead.
  • The page supports an optional id prop. Use it if you need multiple radars, for example .

How do I load in my own data?

To pass own data to plugin use a getData prop which expects a Promise<TechRadarLoaderResponse> signature.

For example:

Copy
const getData = () =>
  Promise.resolve({
    quadrants: [{ id: 'infrastructure', name: 'Infrastructure' }],
    rings: [{ id: 'use', name: 'USE', color: '#91c49d' }],
    entries: [
      {
        moved: 0,
        ring: 'use',
        url: '#',
        key: 'firebase-function',
        id: 'firebase-function',
        title: 'FireBase Function',
        quadrant: 'infrastructure',
      },
    ],
  });

<TechRadarComponent width={1500} height={900} getData={getData} />;

Changelog

This changelog is produced from commits made to the Tech Radar plugin since a year ago. It may not contain information about all commits. Releases and version bumps are intentionally omitted. This changelog is generated by AI.

Breaking changes

  • Introduce Tech Radar backend and common package. This is a breaking change. Add the common package to your app. Update imports if you use the types. The default client now tries the backend then falls back to mock data. #1539 (merged 11 months ago)

Features

  • Render Markdown in timeline entry descriptions. #4018 (merged 3 months ago)
  • Improve Tech Radar responsiveness on small screens. Columns keep a minimum width. The legend shrinks from three to one column as needed. The radar resizes with the screen. It stays centered between the legends. #2474 (merged 5 months ago)
  • Make the new Tech Radar common and backend packages public. #1762 (merged 11 months ago)

Documentation

  • Fix README link to the detailed example. #4020 (merged 4 months ago)
  • Improve Tech Radar docs and sample app setup. #1762 (merged 11 months ago)

Dependencies

  • Update types color to v4. #2231 (merged 9 months ago)
  • Remove unused canvas dev dependency. #3565 (merged 5 months ago)

Maintenance

  • Reduce knip false positives by updating repo tools. #3018 (merged 6 months ago)
  • Clarify if else flow with a no op change. #2520 (merged 8 months ago)

Set up Backstage in minutes with Roadie