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.

Installation Instructions
These instructions apply to self-hosted Backstage only. To use this plugin on Roadie, visit the docs.
Install the frontend package
- From your Backstage root directory run
yarn --cwd packages/app add @backstage-community/plugin-tech-radar
Add the page route in a legacy frontend app
- Edit packages/app/src/App.tsx
- Import the page
// packages/app/src/App.tsx
import React from 'react';
import { FlatRoutes, Route } from '@backstage/core-app-api';
import { TechRadarPage } from '@backstage-community/plugin-tech-radar';
// other imports
export const AppRoutes = () => (
<FlatRoutes>
{/* other routes */}
<Route path="/tech-radar" element={<TechRadarPage />} />
</FlatRoutes>
);
You can customize the page if you want
// packages/app/src/App.tsx
<Route
path="/tech-radar"
element={
<TechRadarPage
title="Tech Radar"
subtitle="Engineering choices"
pageTitle="Tech Radar"
width={1200}
height={800}
/>
}
/>
Add the page using the new frontend system
- Import the page extension and register it
// packages/app/src/App.tsx
import React from 'react';
import { createApp, createFrontendModule } from '@backstage/frontend-app-api';
import { techRadarPage } from '@backstage-community/plugin-tech-radar/alpha';
export const app = createApp({
features: [
createFrontendModule({
pluginId: 'app',
extensions: [
techRadarPage({
path: '/tech-radar',
// optional presentation settings
title: 'Tech Radar',
subtitle: 'Engineering choices',
pageTitle: 'Tech Radar',
width: 1200,
height: 800,
}),
],
}),
],
});
Install the backend plugin in a legacy backend app
The frontend defaults to loading data from the tech radar backend. Install it.
- Add the package to the backend
yarn --cwd packages/backend add @backstage-community/plugin-tech-radar-backend
- Create a router loader
// 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,
discovery: env.discovery,
cache: env.cache,
reader: env.reader,
});
}
- Wire it in the backend index
// packages/backend/src/index.ts
import techRadar from './plugins/techRadar';
// other imports
async function main() {
// existing code that creates apiRouter and envs
const techRadarEnv = useHotMemoize(module, () => createEnv('tech-radar'));
apiRouter.use('/tech-radar', await techRadar(techRadarEnv));
// existing server start
}
- Configure a data source url so the backend can fetch the radar data
# app-config.yaml
techRadar:
url: https://example.com/path/to/tech-radar.json
Place a JSON at that url that follows the model shown further below.
Install the backend plugin in the new backend system
- Add the package
yarn --cwd packages/backend add @backstage-community/plugin-tech-radar-backend
- Register the module in your backend builder
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
import { techRadarModule } from '@backstage-community/plugin-tech-radar-backend';
const backend = createBackend();
backend.add(techRadarModule());
backend.start();
- Add config for the data source
# app-config.yaml
techRadar:
url: https://example.com/path/to/tech-radar.json
Note that your module name may differ in some templates. If your package exports a different module factory name, import that one instead.
Use a custom data source without installing the backend
You can provide your own client that implements the TechRadarApi. This replaces the default backend client.
Legacy frontend app
// packages/app/src/lib/MyClient.ts
import {
TechRadarApi,
} from '@backstage-community/plugin-tech-radar';
import { TechRadarLoaderResponse } from '@backstage-community/plugin-tech-radar-common';
export class MyOwnClient implements TechRadarApi {
async load(id: string | undefined): Promise<TechRadarLoaderResponse> {
const data = await fetch('https://mydata.example.com/radar.json').then(r => r.json());
return {
...data,
entries: data.entries.map((entry: any) => ({
...entry,
timeline: entry.timeline.map((t: any) => ({
...t,
date: new Date(t.date),
})),
})),
};
}
}
Register the API factory
// packages/app/src/apis.ts
import { createApiFactory } from '@backstage/core-plugin-api';
import { techRadarApiRef } from '@backstage-community/plugin-tech-radar';
import { MyOwnClient } from './lib/MyClient';
export const apis = [
createApiFactory(techRadarApiRef, new MyOwnClient()),
];
New frontend system
// packages/app/src/App.tsx
import { ApiBlueprint, createApp, createFrontendModule } from '@backstage/frontend-app-api';
import { techRadarApiRef } from '@backstage-community/plugin-tech-radar';
import { MyOwnClient } from './lib/MyClient';
const techRadarApi = ApiBlueprint.make({
name: 'techRadarApi',
params: {
factory: createApiFactory(techRadarApiRef, new MyOwnClient()),
},
});
export const app = createApp({
features: [
createFrontendModule({
pluginId: 'app',
extensions: [techRadarApi],
}),
],
});
Use the component directly in a custom page
You can render the component in your own page if you prefer that layout.
Create a page
// packages/app/src/components/TechRadar/CustomTechRadarPage.tsx
import React from 'react';
import { Content, Page, Header } from '@backstage/core-components';
import { TechRadarComponent } from '@backstage-community/plugin-tech-radar';
export const CustomTechRadarPage = () => (
<Page themeId="tool">
<Header title="Tech Radar" />
<Content>
<TechRadarComponent width={1200} height={800} />
</Content>
</Page>
);
Add a route to it
// packages/app/src/App.tsx
import { CustomTechRadarPage } from './components/TechRadar/CustomTechRadarPage';
<FlatRoutes>
{/* other routes */}
<Route path="/tech-radar" element={<CustomTechRadarPage />} />
</FlatRoutes>
Data model example
The backend and the custom client both expect the same shape. Here is a minimal sample.
{
"title": "Engineering Tech Radar",
"quadrants": [
{ "id": "1", "name": "Bottom right" },
{ "id": "2", "name": "Bottom left" },
{ "id": "3", "name": "Top left" },
{ "id": "4", "name": "Top right" }
],
"rings": [
{ "id": "adopt", "name": "ADOPT", "color": "#93c47d" },
{ "id": "trial", "name": "TRIAL", "color": "#93d2c2" },
{ "id": "assess", "name": "ASSESS", "color": "#fbdb84" },
{ "id": "hold", "name": "HOLD", "color": "#efafa9" }
],
"entries": [
{
"id": "typescript",
"title": "Typescript",
"description": "Long description for Typescript",
"key": "typescript",
"url": "#",
"quadrant": "1",
"timeline": [
{ "moved": 1, "ringId": "adopt", "date": "2022-02-08", "description": "Adopted" },
{ "moved": 0, "ringId": "trial", "date": "2022-02-06", "description": "Trial" }
]
}
]
}
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:
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, and based on the code located here. 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
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.