feat(ui): centralize routing in BUIProvider (#33267)

* feat(ui): centralize routing in BUIProvider

BUIProvider now auto-detects React Router context and provides
client-side navigation for all BUI components. Retired
InternalLinkProvider and added BUIRouterProvider as a public
export for integration use.

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* feat(plugin-app): move BUIProvider inside app router

Moved BUIProvider from wrapping AppRouter to being a child inside
it, so it detects the React Router context and provides client-side
routing for all BUI components.

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* feat(core-app-api): add BUIRouterProvider to legacy app router

Added BUIRouterProvider inside the legacy AppRouter to provide
React Aria routing for all BUI components.

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* docs(ui): update BUIProvider documentation for routing

Updated installation docs to cover BUIProvider's routing role
and the requirement to render it inside a React Router context.

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* refactor(ui): move BUIProvider from analytics to provider directory

BUIProvider now handles both analytics and routing, so it no longer
belongs in the analytics directory.

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* fix(ui): add BUIProvider to storybook stories with MemoryRouter

Added BUIProvider inside MemoryRouter in all stories that use
routing, so client-side navigation works in Storybook.

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* fix(plugin-app): move BUIProvider inside RouterComponent

Moved BUIProvider to wrap all content inside RouterComponent
so that extraElements (like dialogs) also get BUI context.

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* refactor: replace BUIRouterProvider with BUIProvider in legacy app

Use BUIProvider directly inside the legacy AppRouter instead of a
separate BUIRouterProvider export. Removes BUIRouterProvider from
the public API of @backstage/ui.

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* refactor(ui): inline routing logic into BUIProvider

Removed the routing/ directory and inlined the RouterProvider
setup directly into BUIProvider since it's the only consumer.

Signed-off-by: Johan Persson <johanopersson@gmail.com>

---------

Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
Johan Persson
2026-03-16 18:56:24 +01:00
committed by GitHub
parent 1058260dc6
commit 42f8c9b2b8
33 changed files with 884 additions and 914 deletions
@@ -49,23 +49,28 @@ your plugin and import the components you need.
<CodeBlock lang="tsx" title="Let's get started 🚀" code={snippet} />
## Analytics
## BUIProvider
BUI components with navigation behavior — Link, ButtonLink, Tab, MenuItem, Tag, and Row — can fire analytics events when clicked. To enable this, you need to connect BUI's analytics layer to Backstage's analytics system.
`BUIProvider` provides routing and analytics integration for all BUI components. It must be rendered inside a React Router context for client-side navigation to work in components like Link, ButtonLink, Tabs, Menu, TagGroup, and Table.
### Setup
If you're using the **new frontend system**, analytics is wired automatically via `@backstage/plugin-app` — no setup required.
If you're using the **new frontend system**, the provider is wired automatically via `@backstage/plugin-app` — no setup required.
For the **old frontend system**, the `BUIProvider` is included in the app shell from `@backstage/core-app-api` and works out of the box.
If you need to set up the provider manually (e.g. in a custom app shell), wrap your app content with the `BUIProvider` and pass in Backstage's `useAnalytics` hook:
If you need to set up the provider manually (e.g. in a custom app shell), wrap your app content with the `BUIProvider` inside your Router and pass in Backstage's `useAnalytics` hook:
<CodeBlock lang="tsx" code={analyticsSetupSnippet} />
### How it works
<Banner
text="BUIProvider must be rendered inside a React Router context. If placed outside, components will fall back to full-page navigation instead of client-side routing."
variant="warning"
/>
Once configured, BUI components fire a `click` event through Backstage's analytics system when a user navigates. Events include the link text as the subject and the destination URL in the attributes, along with any `AnalyticsContext` metadata (such as `pluginId`) from the component's position in the tree.
### Analytics
Once configured, BUI components with navigation behavior — Link, ButtonLink, Tab, MenuItem, Tag, and Row — fire a `click` event through Backstage's analytics system when a user navigates. Events include the link text as the subject and the destination URL in the attributes, along with any `AnalyticsContext` metadata (such as `pluginId`) from the component's position in the tree.
To suppress tracking on an individual component, use the `noTrack` prop:
@@ -7,11 +7,14 @@ export const snippet = `import { Flex, Button, Text } from '@backstage/ui';
export const analyticsSetupSnippet = `import { BUIProvider } from '@backstage/ui';
import { useAnalytics } from '@backstage/core-plugin-api';
import { BrowserRouter } from 'react-router-dom';
// Wrap your app content with the provider
<BUIProvider useAnalytics={useAnalytics}>
<AppContent />
</BUIProvider>`;
// BUIProvider must be inside a Router for client-side navigation
<BrowserRouter>
<BUIProvider useAnalytics={useAnalytics}>
<AppContent />
</BUIProvider>
</BrowserRouter>`;
export const analyticsNoTrackSnippet = `// Suppress analytics for a specific link
<Link href="/internal" noTrack>