diff --git a/.changeset/nav-items-page-discovery.md b/.changeset/nav-items-page-discovery.md new file mode 100644 index 0000000000..11e9ee431d --- /dev/null +++ b/.changeset/nav-items-page-discovery.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-app-react': minor +'@backstage/plugin-app': patch +--- + +Added new `NavItem`, `NavItems`, and `navItems` prop to `NavContentComponentProps` for auto-discovering navigation items from page extensions. The new `navItems` collection supports `take(id)` and `rest()` methods for placing specific items in custom sidebar positions. The existing `items` prop is now deprecated in favor of `navItems`. diff --git a/docs/frontend-system/building-apps/08-migrating.md b/docs/frontend-system/building-apps/08-migrating.md index 3ac5109fc7..e71e171af1 100644 --- a/docs/frontend-system/building-apps/08-migrating.md +++ b/docs/frontend-system/building-apps/08-migrating.md @@ -686,7 +686,7 @@ createApp({ #### App Root Sidebar -New apps feature a built-in sidebar extension which is created by using the `NavContentBlueprint` in `src/modules/nav/Sidebar.tsx`. The default implementation of the sidebar in this blueprint will render some items explicitly in different groups, and then render the rest of the items which are the other `NavItem` extensions provided by the system. +New apps feature a built-in sidebar extension which is created by using the `NavContentBlueprint` in `src/modules/nav/Sidebar.tsx`. The default implementation of the sidebar in this blueprint will render some items explicitly in different groups, and then render the rest of the items. Nav items are auto-discovered from page extensions registered under `app/routes` (no explicit `NavItemBlueprint` required), with metadata from page config, nav item extensions, or plugin defaults. In order to migrate your existing sidebar, you will want to create an override for the `app/nav` extension. You can do this by copying the standard of having a `src/modules/nav/` folder, which can contain an extension which you can install into the `app` in the form of a `module`. @@ -702,38 +702,56 @@ export const navModule = createFrontendModule({ Then in the actual implementation for the `SidebarContent` extension, you can provide something like the following, where you implement the entire `Sidebar` component. +The component receives a `navItems` prop with `take(id)` and `rest()` methods for placing specific items in custom positions. Use `navItems.take('page:home')` to take a specific item by extension ID (e.g. for a header slot), and `navItems.rest()` to get all remaining items. + ```tsx title="in packages/app/src/modules/nav/Sidebar.tsx" import { NavContentBlueprint } from '@backstage/plugin-app-react'; export const SidebarContent = NavContentBlueprint.make({ params: { - component: ({ items }) => ( - - - } to="/search"> - - - - }> - ... - - - - {/* Items in this group will be scrollable if they run out of space */} - {items.map((item, index) => ( - - ))} - - - - ), + component: ({ navItems }) => { + // Take specific items for custom placement + const home = navItems.take('page:home'); + // Get all remaining items + const rest = navItems.rest(); + + return ( + + + {home && ( + + )} + } to="/search"> + + + + }> + ... + + + + {rest.map(item => ( + + ))} + + + + ); + }, }, }); ``` -The `items` property is a list of all extensions provided by the `NavItemBlueprint` that are currently installed in the App. If you don't want to auto populate this list you can simply remove the rendering of that `SidebarGroup`, but otherwise you can see from the above example how a `SidebarItem` element is rendered for each of the items in the list. +The deprecated `items` prop (a flat list compatible with ``) remains supported for backward compatibility. If you don't want to auto-populate the list, simply remove the rendering of that `SidebarGroup`. -You might also notice that when you're rendering additional fixed icons for plugins that these might become duplicated as the plugin provides a `NavItem` extension and you're also rendering one in the `Sidebar` manually. In order to remove the item from the list of `items` which is passed through, we recommend that you disable that extension using config: +You might also notice that when you're rendering additional fixed icons for plugins (e.g. Search in a dedicated group) these might become duplicated, since that page is also included in `navItems.rest()`. To exclude an item from the remaining list, use `navItems.take('page:search')` before calling `navItems.rest()`. Items that have been taken will not appear in `rest()`. + +You can also use the old `NavItemBlueprint`-based nav item extensions to disable items from the nav bar, these can be disabled in config without affecting the page itself: ```yaml title="in app-config.yaml" app: @@ -742,15 +760,6 @@ app: - nav-item:catalog: false ``` -You can also determine the order of the provided auto installed `NavItems` that you get from the system in config. The below example ensures that the `catalog` navigation item will proceed the `search` navigation item when being passed through as the `item` prop. - -```yaml title="in app-config.yaml" -app: - extensions: - - nav-item:catalog - - nav-item:search -``` - #### App Root Routes Your top-level routes are the routes directly under the `AppRouter` component with the `` element. In a small app they might look something like this: diff --git a/docs/frontend-system/building-plugins/03-common-extension-blueprints.md b/docs/frontend-system/building-plugins/03-common-extension-blueprints.md index 876f035bfd..850c3b3f64 100644 --- a/docs/frontend-system/building-plugins/03-common-extension-blueprints.md +++ b/docs/frontend-system/building-plugins/03-common-extension-blueprints.md @@ -15,9 +15,9 @@ These are the [extension blueprints](../architecture/23-extension-blueprints.md) An API extension is used to add or override [Utility API factories](../utility-apis/01-index.md) in the app. They are commonly used by plugins for both internal and shared APIs. There are also many built-in Api extensions provided by the framework that you are able to override. -### NavItem - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.NavItemBlueprint.html) +### NavItem (deprecated) - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.NavItemBlueprint.html) -Navigation item extensions are used to provide menu items that link to different parts of the app. By default nav items are attached to the app nav extension, which by default is rendered as the left sidebar in the app. +The `NavItemBlueprint` is deprecated. The app now auto-discovers navigation items from page extensions, so explicit nav item extensions are no longer needed. To migrate, ensure your plugin and/or page extensions have a `title` and `icon` set — these are used to populate the sidebar automatically. ### Page - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.PageBlueprint.html) @@ -51,10 +51,12 @@ Icon bundle extensions provide the ability to replace or provide new icons to th Translation extension provide custom translation messages for the app. They can be used both to override the default english messages to custom ones, as well as provide translations for additional languages. -### NavContent - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.NavContentBlueprint.html) +### NavContent - [Reference](https://backstage.io/api/stable/variables/_backstage_plugin-app-react.NavContentBlueprint.html) Nav content extensions allow you to replace the entire navbar with your own component. They are always attached to the app nav extension. +Your custom component receives a `navItems` prop—a collection with `take(id)` and `rest()` methods for placing specific items in custom positions. Nav items are auto-discovered from page extensions, and metadata (title, icon) comes from page config, nav item extensions, or plugin defaults. Use `navItems.take('page:home')` to take a specific item by extension ID, and `navItems.rest()` to get all remaining items. The deprecated `items` prop (a flat list) remains supported for backward compatibility. + ### Router - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.RouterBlueprint.html) Router extensions allow you to replace the router component used by the app. They are always attached to the app root extension.