Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@red-hat-developer-hub/backstage-plugin-global-header': minor
---

**BREAKING**: Import building blocks (`GlobalHeaderIconButton`, `GlobalHeaderMenuItem`, `GlobalHeaderDropdown`) only from `@red-hat-developer-hub/backstage-plugin-global-header/components` — they are no longer re-exported from the root entry or deprecated `/alpha`. Prefer dynamic `import()` inside blueprint loaders.

Building-block UI lives solely on the `/components` Module Federation expose (`src/componentsExport.ts`) so MUI stays off the root NFS sync chunk. MUI `ClassNameGenerator` setup is also moved off the root sync path: it runs from `configureMuiClassName.ts` when the lazy AppBar / `/components` / `/legacy` UI loads, not from the root entry.
13 changes: 10 additions & 3 deletions workspaces/global-header/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@ By default it includes a Search input field, Create, Starred[^1], Support[^2] an

The plugin supports two integration modes:

- **New Frontend System** -- Extension blueprints (`GlobalHeaderComponentBlueprint`, `GlobalHeaderMenuItemBlueprint`) allow any plugin to contribute toolbar items and dropdown menu items. See the [New Frontend System Guide](new-frontend-system.md) for full details, including architecture, code examples, and API reference.
- **Legacy Mount Points** -- Dynamic plugin mount points for traditional Backstage apps. See [Configuration](configuration.md).
- **New Frontend System (stable)** -- Import from the package root
(`@red-hat-developer-hub/backstage-plugin-global-header`). Extension blueprints
(`GlobalHeaderComponentBlueprint`, `GlobalHeaderMenuItemBlueprint`) allow any
plugin to contribute toolbar items and dropdown menu items. Building-block UI
belongs on `/components` so it stays off the main NFS sync chunk. See the
[New Frontend System Guide](new-frontend-system.md) for full details,
including architecture, code examples, and API reference.
- **Legacy Mount Points** -- Import from `/legacy` for traditional Backstage
apps using dynamic plugin mount points. See [Configuration](configuration.md).

Deployers can also add menu items directly via `app-config.yaml` without writing any plugin code. See [Config-Driven Menu Items](new-frontend-system.md#config-driven-menu-items).
Deployers can also add menu items directly via `app-config.yaml` without writing any plugin code. See [Config-Driven Menu Items](new-frontend-system.md#add-items-from-app-configyaml).

[^1]: Only when an entity is starred.
[^2]: Only when the Support URL is configured in the `app-config.yaml`.
Expand Down
138 changes: 90 additions & 48 deletions workspaces/global-header/docs/new-frontend-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

The global header is the top-level navigation bar in Red Hat Developer Hub. It ships with sensible defaults -- a company logo, search, notifications, and user profile -- but is designed to be extended by other plugins and configured by deployers.

The NFS surface is **stable** on the package root
(`@red-hat-developer-hub/backstage-plugin-global-header`). Building-block UI
components live on a separate `/components` entry so they stay off the main
Module Federation sync chunk. Prefer `/legacy` only for Old Frontend System
(mount-point) apps. `/alpha` is a deprecated translations-only shim — do not
use it for the plugin, module, or blueprints.

This guide explains how to:

- [Set up the header in your app](#setup)
Expand Down Expand Up @@ -39,7 +46,7 @@ yarn --cwd packages/app add @red-hat-developer-hub/backstage-plugin-global-heade
import { createApp } from '@backstage/frontend-defaults';
import globalHeaderPlugin, {
globalHeaderModule,
} from '@red-hat-developer-hub/backstage-plugin-global-header/alpha';
} from '@red-hat-developer-hub/backstage-plugin-global-header';

export default createApp({
features: [
Expand All @@ -54,14 +61,15 @@ Both are required. The **module** provides the wrapper; the **plugin** provides

## Add a toolbar component

Import `GlobalHeaderComponentBlueprint` and call `.make()`. There are three ways to define what renders.
Import `GlobalHeaderComponentBlueprint` from the root and call `.make()`.
There are three ways to define what renders.

### Option A: Provide data, let the framework render

Supply `icon`, `title`, and `link` (or `onClick`). The framework renders a styled icon button for you.

```typescript
import { GlobalHeaderComponentBlueprint } from '@red-hat-developer-hub/backstage-plugin-global-header/alpha';
import { GlobalHeaderComponentBlueprint } from '@red-hat-developer-hub/backstage-plugin-global-header';

export const myButton = GlobalHeaderComponentBlueprint.make({
name: 'my-button',
Expand All @@ -76,26 +84,31 @@ export const myButton = GlobalHeaderComponentBlueprint.make({

### Option B: Use building-block components

For dropdowns or more control, provide a `component` that uses the exported building blocks (`GlobalHeaderIconButton`, `GlobalHeaderDropdown`).
For dropdowns or more control, provide a `loader` (preferred) that dynamically
imports building blocks from `/components` (kept off the root NFS sync
chunk):

```typescript
import {
GlobalHeaderComponentBlueprint,
GlobalHeaderDropdown,
} from '@red-hat-developer-hub/backstage-plugin-global-header/alpha';

const MyDropdown = () => (
<GlobalHeaderDropdown
target="my-links"
isIconButton
tooltip="My links"
buttonContent={<MyIcon />}
/>
);
import { GlobalHeaderComponentBlueprint } from '@red-hat-developer-hub/backstage-plugin-global-header';

export const myDropdown = GlobalHeaderComponentBlueprint.make({
name: 'my-dropdown',
params: { component: MyDropdown, priority: 75 },
params: {
priority: 75,
loader: async () => {
const { GlobalHeaderDropdown } = await import(
'@red-hat-developer-hub/backstage-plugin-global-header/components'
);
return () => (
<GlobalHeaderDropdown
target="my-links"
isIconButton
tooltip="My links"
buttonContent={<MyIcon />}
/>
);
},
},
});
```

Expand All @@ -116,17 +129,18 @@ export const myWidget = GlobalHeaderComponentBlueprint.make({

### Parameters reference

| Param | Type | Description |
| ----------- | ------------------------- | ----------------------------------------------- |
| `icon` | `string` | Icon name, inline SVG, or URL |
| `title` | `string` | Display title (also tooltip and aria-label) |
| `titleKey` | `string` | i18n translation key for the title |
| `tooltip` | `string` | Explicit tooltip (overrides `title`) |
| `link` | `string` | Navigation URL |
| `onClick` | `() => void` | Click handler (mutually exclusive with `link`) |
| `component` | `ComponentType` | Custom React component (options B/C) |
| `priority` | `number` | Sort order -- higher values appear further left |
| `layout` | `Record<string, unknown>` | MUI `sx` overrides on the wrapper |
| Param | Type | Description |
| ----------- | ------------------------------ | ----------------------------------------------------- |
| `icon` | `string` | Icon name, inline SVG, or URL |
| `title` | `string` | Display title (also tooltip and aria-label) |
| `titleKey` | `string` | i18n translation key for the title |
| `tooltip` | `string` | Explicit tooltip (overrides `title`) |
| `link` | `string` | Navigation URL |
| `onClick` | `() => void` | Click handler (mutually exclusive with `link`) |
| `component` | `ComponentType` | Custom React component (options B/C; prefer `loader`) |
| `loader` | `() => Promise<ComponentType>` | Async component factory (keeps UI off sync) |
| `priority` | `number` | Sort order -- higher values appear further left |
| `layout` | `Record<string, unknown>` | MUI `sx` overrides on the wrapper |

## Add a menu item

Expand All @@ -143,7 +157,7 @@ Import `GlobalHeaderMenuItemBlueprint`. The `target` field routes the item to th
Provide `title`, `link`, and optionally `icon` / `sectionLabel`. Items that share a `sectionLabel` are grouped under that heading.

```typescript
import { GlobalHeaderMenuItemBlueprint } from '@red-hat-developer-hub/backstage-plugin-global-header/alpha';
import { GlobalHeaderMenuItemBlueprint } from '@red-hat-developer-hub/backstage-plugin-global-header';

export const docsItem = GlobalHeaderMenuItemBlueprint.make({
name: 'my-docs',
Expand All @@ -160,26 +174,38 @@ export const docsItem = GlobalHeaderMenuItemBlueprint.make({

### Custom component item using building blocks

Use the exported `GlobalHeaderMenuItem` to build a complete, clickable menu item with consistent styling. The component receives `handleClose` and `hideDivider` as props from the dropdown.
Use `GlobalHeaderMenuItem` from `/components` inside a blueprint `loader` so the
UI stays off the root NFS sync chunk. The component receives
`handleClose` and `hideDivider` as props from the dropdown.

```typescript
import {
GlobalHeaderMenuItemBlueprint,
GlobalHeaderMenuItem,
} from '@red-hat-developer-hub/backstage-plugin-global-header/alpha';

const MyDocsLink = ({ handleClose }: { handleClose?: () => void }) => (
<GlobalHeaderMenuItem
to="https://docs.example.com"
title="Documentation"
icon="menu_book"
onClick={handleClose}
/>
);
import { GlobalHeaderMenuItemBlueprint } from '@red-hat-developer-hub/backstage-plugin-global-header';

export const myDocsItem = GlobalHeaderMenuItemBlueprint.make({
name: 'my-docs-link',
params: { target: 'help', component: MyDocsLink, priority: 50 },
params: {
target: 'help',
priority: 50,
loader: async () => {
const { GlobalHeaderMenuItem } = await import(
'@red-hat-developer-hub/backstage-plugin-global-header/components'
);
return function MyDocsLink({
handleClose,
}: {
handleClose?: () => void;
}) {
return (
<GlobalHeaderMenuItem
to="https://docs.example.com"
title="Documentation"
icon="menu_book"
onClick={handleClose}
/>
);
};
},
},
});
```

Expand Down Expand Up @@ -224,7 +250,7 @@ import { createFrontendPlugin } from '@backstage/frontend-plugin-api';
import {
GlobalHeaderComponentBlueprint,
GlobalHeaderMenuItemBlueprint,
} from '@red-hat-developer-hub/backstage-plugin-global-header/alpha';
} from '@red-hat-developer-hub/backstage-plugin-global-header';

export default createFrontendPlugin({
pluginId: 'my-plugin',
Expand Down Expand Up @@ -346,14 +372,25 @@ Extension ID pattern: `gh-menu-item:global-header/<name>`

For plugin authors building custom toolbar components or dropdowns, the plugin exports lower-level building blocks and React hooks:

**Building-block components** (consistent styling without starting from scratch):
**Building-block components** (import from `/components` — not the root
entry — so MUI stays off the main NFS sync chunk):

| Component | Key props | Purpose |
| ------------------------ | -------------------------------- | ---------------------------------------------------------------------------- |
| `GlobalHeaderIconButton` | `title`, `icon`, `to` | Toolbar icon button that navigates to a URL |
| `GlobalHeaderMenuItem` | `to`, `title`, `icon`, `onClick` | Complete clickable menu item with link navigation and consistent styling |
| `GlobalHeaderDropdown` | `target`, `buttonContent` | Dropdown that auto-collects `gh-menu-item` extensions for the given `target` |

```typescript
import {
GlobalHeaderIconButton,
GlobalHeaderMenuItem,
GlobalHeaderDropdown,
} from '@red-hat-developer-hub/backstage-plugin-global-header/components';
```

Prefer dynamic `import()` of these from inside blueprint `loader`s.

**Context hooks** (direct access to collected extension data):

| Hook | Returns |
Expand All @@ -363,4 +400,9 @@ For plugin authors building custom toolbar components or dropdowns, the plugin e

**Translations:** Use `titleKey` / `subTitleKey` for i18n. Keys containing dots (e.g. `'applicationLauncher.sections.documentation'`) are auto-resolved. The plugin exports `globalHeaderTranslationRef` and `globalHeaderTranslations` for overrides.

All exports are available from `@red-hat-developer-hub/backstage-plugin-global-header/alpha`.
| Entry | Use for |
| ------------------ | ----------------------------------------------------------------- |
| Package root (`.`) | NFS plugin, module, blueprints, hooks, translations |
| `/components` | Building-block UI only (`GlobalHeaderMenuItem`, dropdowns, icons) |
| `/legacy` | Deprecated OFS / mount-point API |
| `/alpha` | Deprecated translations re-export only (not an NFS entry) |
32 changes: 21 additions & 11 deletions workspaces/global-header/packages/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
globalHeaderModule,
globalHeaderTranslationsModule,
GlobalHeaderMenuItemBlueprint,
GlobalHeaderMenuItem,
} from '@red-hat-developer-hub/backstage-plugin-global-header';
import { navModule } from './modules/nav';

Expand Down Expand Up @@ -57,24 +56,35 @@ const signInModule = createFrontendModule({
],
});

const CustomHelpMenuItem = ({ handleClose }: { handleClose?: () => void }) => (
<GlobalHeaderMenuItem
to="https://backstage.io/docs"
title="Backstage Docs"
icon="menu_book"
onClick={handleClose}
/>
);

const headerExamplesPlugin = createFrontendPlugin({
pluginId: 'header-examples',
extensions: [
GlobalHeaderMenuItemBlueprint.make({
name: 'custom-help-docs',
params: {
target: 'help',
component: CustomHelpMenuItem,
priority: 50,
// Import building blocks from `/components` inside the loader so they
// stay off the main `/alpha` NFS sync chunk.
loader: async () => {
const { GlobalHeaderMenuItem } = await import(
'@red-hat-developer-hub/backstage-plugin-global-header/components'
);
return function CustomHelpMenuItem({
handleClose,
}: {
handleClose?: () => void;
}) {
return (
<GlobalHeaderMenuItem
to="https://backstage.io/docs"
title="Backstage Docs"
icon="menu_book"
onClick={handleClose}
/>
);
};
},
},
}),
],
Expand Down
19 changes: 15 additions & 4 deletions workspaces/global-header/plugins/global-header/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ yarn --cwd packages/app add @red-hat-developer-hub/backstage-plugin-global-heade

### New Frontend System

Import the plugin and module in your NFS app:
The NFS plugin is available from the package root (`.`). Import the
plugin and module in your NFS app:

```typescript
import { createApp } from '@backstage/frontend-defaults';
import globalHeaderPlugin, {
globalHeaderModule,
} from '@red-hat-developer-hub/backstage-plugin-global-header/alpha';
} from '@red-hat-developer-hub/backstage-plugin-global-header';

export default createApp({
features: [
Expand All @@ -38,11 +39,21 @@ export default createApp({
});
```

Other plugins can contribute toolbar items and dropdown menu items using `GlobalHeaderComponentBlueprint` and `GlobalHeaderMenuItemBlueprint`. See the [New Frontend System documentation](../../docs/new-frontend-system.md) for detailed examples and API reference.
> `/alpha` is deprecated and translations-only — use the root import for NFS.
> Legacy (OFS) mounts live under `/legacy`.

Other plugins can contribute toolbar items and dropdown menu items using
`GlobalHeaderComponentBlueprint` and `GlobalHeaderMenuItemBlueprint` from the
root. Building-block UI (`GlobalHeaderMenuItem`, `GlobalHeaderDropdown`,
…) must be imported from `/components` (ideally inside a blueprint `loader`) so
MUI-heavy UI stays off the main NFS Module Federation sync chunk. See the
[New Frontend System documentation](../../docs/new-frontend-system.md) for
detailed examples and API reference.

### Legacy (Mount Points)

For legacy Backstage apps using dynamic plugin mount points, see the [Configuration documentation](../../docs/configuration.md).
For legacy Backstage apps using dynamic plugin mount points, import from
`/legacy` and see the [Configuration documentation](../../docs/configuration.md).

## Configuration

Expand Down
5 changes: 5 additions & 0 deletions workspaces/global-header/plugins/global-header/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"./legacy": "./src/legacy.ts",
"./global-header-module": "./src/globalHeaderModuleExport.ts",
"./global-header-translations-module": "./src/globalHeaderTranslationsModuleExport.ts",
"./components": "./src/componentsExport.ts",
"./package.json": "./package.json"
},
"typesVersions": {
Expand All @@ -25,6 +26,9 @@
"global-header-translations-module": [
"src/globalHeaderTranslationsModuleExport.ts"
],
"components": [
"src/componentsExport.ts"
],
"package.json": [
"package.json"
]
Expand Down Expand Up @@ -123,6 +127,7 @@
"PluginRoot": "./src/index.ts",
"Alpha": "./src/alpha/index.ts",
"Legacy": "./src/legacy.ts",
"Components": "./src/componentsExport.ts",
"GlobalHeaderModule": "./src/globalHeaderModuleExport.ts",
"GlobalHeaderTranslationsModule": "./src/globalHeaderTranslationsModuleExport.ts"
}
Expand Down
Loading
Loading