docs/getting-started/homepage.md
::::info This documentation is written for the new frontend system, which is the default in new Backstage apps. If your Backstage app still uses the old frontend system, read the old frontend system version of this guide instead. ::::
The Home plugin gives your Backstage app a homepage where users can find what they need without memorizing URLs. It ships with a drag-and-drop grid layout and a set of built-in widgets. Users can add, remove, rearrange, and resize widgets, and their layout is saved per user.
This guide covers:
Before you begin, make sure:
@backstage/create-app
and not using a fork of the
backstage repository.yarn --cwd packages/app add @backstage/plugin-home
Once installed, the plugin is available in your app through default feature discovery. See installing plugins for alternative installation methods.
The homepage lives at /home by default. To make it your landing page at /,
add this to your app-config.yaml:
app:
extensions:
- page:home:
config:
path: /
The plugin adds a "Home" navigation item to your sidebar automatically.
Visit tracking records which pages users navigate to. The Most Visited and Recently Visited widgets use this data. It is disabled by default.
When enabled, visit data is stored in one of two places:
To enable it, add these extensions to your app-config.yaml:
app:
extensions:
- api:home/visits: true
- app-root-element:home/visit-listener: true
The following widgets are available out of the box and appear in the Add Widget dialog when editing the homepage.
These widgets come from @backstage/plugin-home:
| Widget | Extension ID | Description |
|---|---|---|
| Starred Entities | home-page-widget:home/starred-entities | Shows entities you have starred in the catalog. |
| Toolkit | home-page-widget:home/toolkit | A collection of configurable links and tools. |
| World Clocks | home-page-widget:home/world-clock | Displays clocks for configured time zones. |
| Most Visited | home-page-widget:home/most-visited | Shows your most frequently visited pages. Requires visit tracking. |
| Recently Visited | home-page-widget:home/recently-visited | Shows pages you have recently visited. Requires visit tracking. |
| Random Joke | home-page-widget:home/random-joke | Shows a random programming joke. |
This widget comes from @backstage/plugin-search:
| Widget | Extension ID | Description |
|---|---|---|
| Search Bar | home-page-widget:search/search-bar | A search bar that navigates to the search page on submit. |
:::note
The search bar widget requires @backstage/plugin-search to be installed.
:::
The Backstage community-plugins repository
hosts additional plugins, some of which provide homepage widgets. Any plugin can
contribute widgets to the homepage by using the HomePageWidgetBlueprint from
@backstage/plugin-home-react/alpha.
Some widgets accept configuration through app-config.yaml. Target a widget
using its extension ID.
The Toolkit widget shows a grid of links. You can configure the links and their icons:
app:
extensions:
- home-page-widget:home/toolkit:
config:
tools:
- url: https://backstage.io/docs
label: Docs
icon: docs
- url: https://github.com/backstage/backstage
label: GitHub
icon: github
- url: https://backstage.io/plugins
label: Plugins Directory
icon: kind:component
The icon field resolves through the app's icon API. You can use any registered
icon, including kind: prefixed icons for catalog entity kinds.
Configure which time zones to display and the time format:
app:
extensions:
- home-page-widget:home/world-clock:
config:
customTimeFormat:
hour12: false
clockConfigs:
- label: NYC
timeZone: America/New_York
- label: UTC
timeZone: UTC
- label: STO
timeZone: Europe/Stockholm
- label: TYO
timeZone: Asia/Tokyo
To hide a widget from the Add Widget dialog, set it to false:
app:
extensions:
- home-page-widget:home/random-joke: false
The defaultConfig option on page:home defines the grid layout that users see
before they have customized anything. Each entry places a widget at a specific
position and size in the grid:
app:
extensions:
- page:home:
config:
path: /
defaultConfig:
- component: HomePageSearchBar
column: 0
row: 0
width: 12
height: 2
deletable: false
- component: HomePageStarredEntities
column: 0
row: 2
width: 4
height: 4
- component: HomePageToolkit
column: 4
row: 2
width: 4
height: 3
- component: HomePageWorldClock
column: 8
row: 2
width: 4
height: 3
Each item in defaultConfig accepts these properties:
| Property | Type | Description |
|---|---|---|
component | string | The widget's component name (the name parameter from the blueprint). |
column | number | The column position in the grid (0-based). |
row | number | The row position in the grid (0-based). |
width | number | The width in grid columns. The default grid has 12 columns. |
height | number | The height in grid rows. |
movable | boolean | Whether the user can move the widget. Defaults to true. |
deletable | boolean | Whether the user can remove the widget. Defaults to true. |
resizable | boolean | Whether the user can resize the widget. Defaults to true. |
:::tip
In edit mode, each widget displays its column, row, width, and height values.
Use these to figure out the right numbers for your defaultConfig.
:::
You can add your own widgets using the HomePageWidgetBlueprint from
@backstage/plugin-home-react/alpha. Define the widget, wrap it in a frontend
module, and register it in your app.
import { createFrontendModule } from '@backstage/frontend-plugin-api';
import { HomePageWidgetBlueprint } from '@backstage/plugin-home-react/alpha';
const myWidget = HomePageWidgetBlueprint.make({
name: 'my-widget',
params: {
name: 'MyWidget',
title: 'My Custom Widget',
description: 'A short description shown in the Add Widget dialog',
components: () =>
import('./MyWidgetComponent').then(m => ({
Content: m.Content,
})),
},
});
export const homeModule = createFrontendModule({
pluginId: 'home',
extensions: [myWidget],
});
Then register the module in your app:
import { homeModule } from './modules/home';
export default createApp({
features: [homeModule],
});
Set minimum and maximum dimensions so the widget does not get too small or too large:
const myWidget = HomePageWidgetBlueprint.make({
name: 'my-widget',
params: {
name: 'MyWidget',
title: 'My Custom Widget',
description: 'A widget with size constraints',
components: () =>
import('./MyWidgetComponent').then(m => ({
Content: m.Content,
})),
layout: {
height: { minRows: 4 },
width: { minColumns: 3 },
},
},
});
Widgets can expose per-user settings. The settings schema follows react-jsonschema-form conventions:
const myWidget = HomePageWidgetBlueprint.make({
name: 'my-widget',
params: {
name: 'MyWidget',
title: 'My Custom Widget',
description: 'A widget with user-configurable settings',
components: () =>
import('./MyWidgetComponent').then(m => ({
Content: m.Content,
Settings: m.Settings,
})),
settings: {
schema: {
title: 'Widget Settings',
type: 'object',
properties: {
color: {
title: 'Color',
type: 'string',
default: 'blue',
enum: ['blue', 'red', 'green'],
},
},
},
},
},
});
If the default grid does not fit your needs, you can replace it entirely. Use
the HomePageLayoutBlueprint from @backstage/plugin-home-react/alpha to
create a layout component that receives the installed widgets and renders them
however you want.
import { createFrontendModule } from '@backstage/frontend-plugin-api';
import {
HomePageLayoutBlueprint,
type HomePageLayoutProps,
} from '@backstage/plugin-home-react/alpha';
import { CustomHomepageGrid } from '@backstage/plugin-home';
import { Content, Header, Page } from '@backstage/core-components';
import { Fragment } from 'react';
const myLayout = HomePageLayoutBlueprint.make({
params: {
loader: async () =>
function MyHomePageLayout({ widgets }: HomePageLayoutProps) {
return (
<Page themeId="home">
<Header title="Welcome" />
<Content>
<CustomHomepageGrid>
{widgets.map((widget, index) => (
<Fragment key={widget.name ?? index}>
{widget.component}
</Fragment>
))}
</CustomHomepageGrid>
</Content>
</Page>
);
},
},
});
export const homeModule = createFrontendModule({
pluginId: 'home',
extensions: [myLayout],
});
When no custom layout is installed, the plugin falls back to a built-in default
that renders widgets inside CustomHomepageGrid.
By default, users can add multiple instances of the same widget. If you are
using a custom layout with CustomHomepageGrid, you can restrict each widget
to a single instance by passing the preventDuplicateWidgets prop. This option
requires a custom layout. It is not exposed as an app-config setting.
<CustomHomepageGrid preventDuplicateWidgets>
{widgets.map((widget, index) => (
<Fragment key={widget.name ?? index}>{widget.component}</Fragment>
))}
</CustomHomepageGrid>