Remove NavItemBlueprint in favor of page-based nav discovery

Drop the deprecated NavItemBlueprint from the public API and migrate core
plugins to set title and icon on PageBlueprint instead. AppNav keeps
backward compatibility for legacy nav-item extensions via an internal
core.nav-item.target data ref.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Patrik Oldsberg
2026-05-19 11:00:46 +02:00
parent 6651f2be06
commit 10e5d6f8aa
45 changed files with 165 additions and 905 deletions
+12 -65
View File
@@ -7,47 +7,21 @@ import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/frontend-plugin-api';
import { IconElement } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { RouteRef as RouteRef_2 } from '@backstage/core-plugin-api';
import { RouteRef } from '@backstage/core-plugin-api';
import { RouteRef as RouteRef_2 } from '@backstage/frontend-plugin-api';
import { TranslationRef } from '@backstage/frontend-plugin-api';
// @alpha (undocumented)
const _default: OverridableFrontendPlugin<
{
root: RouteRef_2<undefined>;
root: RouteRef<undefined>;
},
{},
{
'nav-item:user-settings': OverridableExtensionDefinition<{
kind: 'nav-item';
name: undefined;
config: {
title: string | undefined;
};
configInput: {
title?: string | undefined;
};
output: ExtensionDataRef<
{
title: string;
icon: IconComponent;
routeRef: RouteRef<undefined>;
},
'core.nav-item.target',
{}
>;
inputs: {};
params: {
title: string;
icon: IconComponent;
routeRef: RouteRef<undefined>;
};
}>;
'page:user-settings': OverridableExtensionDefinition<{
kind: 'page';
name: undefined;
@@ -62,7 +36,7 @@ const _default: OverridableFrontendPlugin<
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
@@ -88,7 +62,7 @@ const _default: OverridableFrontendPlugin<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef<AnyRouteRefParams>,
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
@@ -120,7 +94,7 @@ const _default: OverridableFrontendPlugin<
title?: string;
icon?: IconElement;
loader?: () => Promise<JSX_2.Element>;
routeRef?: RouteRef;
routeRef?: RouteRef_2;
noHeader?: boolean;
};
}>;
@@ -136,7 +110,7 @@ const _default: OverridableFrontendPlugin<
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
@@ -168,7 +142,7 @@ const _default: OverridableFrontendPlugin<
title: string;
icon?: IconElement;
loader: () => Promise<JSX.Element>;
routeRef?: RouteRef;
routeRef?: RouteRef_2;
};
}>;
'sub-page:user-settings/feature-flags': OverridableExtensionDefinition<{
@@ -185,7 +159,7 @@ const _default: OverridableFrontendPlugin<
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
@@ -206,7 +180,7 @@ const _default: OverridableFrontendPlugin<
title: string;
icon?: IconElement;
loader: () => Promise<JSX.Element>;
routeRef?: RouteRef;
routeRef?: RouteRef_2;
};
}>;
'sub-page:user-settings/general': OverridableExtensionDefinition<{
@@ -223,7 +197,7 @@ const _default: OverridableFrontendPlugin<
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
@@ -244,40 +218,13 @@ const _default: OverridableFrontendPlugin<
title: string;
icon?: IconElement;
loader: () => Promise<JSX.Element>;
routeRef?: RouteRef;
routeRef?: RouteRef_2;
};
}>;
}
>;
export default _default;
// @alpha (undocumented)
export const settingsNavItem: OverridableExtensionDefinition<{
kind: 'nav-item';
name: undefined;
config: {
title: string | undefined;
};
configInput: {
title?: string | undefined;
};
output: ExtensionDataRef<
{
title: string;
icon: IconComponent;
routeRef: RouteRef<undefined>;
},
'core.nav-item.target',
{}
>;
inputs: {};
params: {
title: string;
icon: IconComponent;
routeRef: RouteRef<undefined>;
};
}>;
// @alpha @deprecated (undocumented)
export const userSettingsTranslationRef: TranslationRef<
'user-settings',
+1 -11
View File
@@ -18,7 +18,6 @@ import {
createExtensionInput,
createFrontendPlugin,
PageBlueprint,
NavItemBlueprint,
SubPageBlueprint,
} from '@backstage/frontend-plugin-api';
import { Content } from '@backstage/core-components';
@@ -38,6 +37,7 @@ const userSettingsPage = PageBlueprint.make({
path: '/settings',
routeRef: settingsRouteRef,
title: 'Settings',
icon: <SettingsIcon fontSize="inherit" />,
},
});
@@ -95,15 +95,6 @@ const featureFlagsSettingsPage = SubPageBlueprint.make({
},
});
/** @alpha */
export const settingsNavItem = NavItemBlueprint.make({
params: {
routeRef: settingsRouteRef,
title: 'Settings',
icon: SettingsIcon,
},
});
/**
* @alpha
*/
@@ -117,7 +108,6 @@ export default createFrontendPlugin({
generalSettingsPage,
authProvidersSettingsPage,
featureFlagsSettingsPage,
settingsNavItem,
],
routes: {
root: settingsRouteRef,