|
| 1 | +# Extending the Global Header (New Frontend System) |
| 2 | + |
| 3 | +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. |
| 4 | + |
| 5 | +This guide explains how to: |
| 6 | + |
| 7 | +- [Set up the header in your app](#setup) |
| 8 | +- [Add a button or widget to the toolbar](#add-a-toolbar-component) |
| 9 | +- [Add a link or section to a dropdown menu](#add-a-menu-item) |
| 10 | +- [Add items from app-config.yaml (no code)](#add-items-from-app-configyaml) |
| 11 | +- [Reorder, override, or disable defaults](#customize-defaults) |
| 12 | + |
| 13 | +## How it works |
| 14 | + |
| 15 | +The header uses two extension kinds: |
| 16 | + |
| 17 | +| Kind | What it contributes | Example | |
| 18 | +| -------------- | -------------------------------------------------------- | --------------------------------------- | |
| 19 | +| `gh-component` | A toolbar-level element (button, dropdown, logo, spacer) | Search bar, notifications icon | |
| 20 | +| `gh-menu-item` | An item inside a dropdown menu | "Settings" link in the profile dropdown | |
| 21 | + |
| 22 | +Extensions are collected at startup, sorted by `priority` (higher = first), and rendered. Menu items from `app-config.yaml` are merged in at runtime. |
| 23 | + |
| 24 | +```mermaid |
| 25 | +flowchart LR |
| 26 | + plugins["Your plugins"] -->|"gh-component / gh-menu-item"| module["globalHeaderModule"] |
| 27 | + config["app-config.yaml"] -->|"globalHeader.menuItems"| module |
| 28 | + module --> header["Rendered header bar"] |
| 29 | +``` |
| 30 | + |
| 31 | +## Setup |
| 32 | + |
| 33 | +```bash |
| 34 | +yarn --cwd packages/app add @red-hat-developer-hub/backstage-plugin-global-header |
| 35 | +``` |
| 36 | + |
| 37 | +```typescript |
| 38 | +// packages/app/src/App.tsx |
| 39 | +import { createApp } from '@backstage/frontend-defaults'; |
| 40 | +import globalHeaderPlugin, { |
| 41 | + globalHeaderModule, |
| 42 | +} from '@red-hat-developer-hub/backstage-plugin-global-header/alpha'; |
| 43 | + |
| 44 | +export default createApp({ |
| 45 | + features: [ |
| 46 | + globalHeaderModule, // Collects extensions and renders the header |
| 47 | + globalHeaderPlugin, // Ships the default toolbar + menu items |
| 48 | + // ...your other plugins |
| 49 | + ], |
| 50 | +}); |
| 51 | +``` |
| 52 | + |
| 53 | +Both are required. The **module** provides the wrapper; the **plugin** provides the default set of components and menu items. |
| 54 | + |
| 55 | +## Add a toolbar component |
| 56 | + |
| 57 | +Import `GlobalHeaderComponentBlueprint` and call `.make()`. There are three ways to define what renders. |
| 58 | + |
| 59 | +### Option A: Provide data, let the framework render |
| 60 | + |
| 61 | +Supply `icon`, `title`, and `link` (or `onClick`). The framework renders a styled icon button for you. |
| 62 | + |
| 63 | +```typescript |
| 64 | +import { GlobalHeaderComponentBlueprint } from '@red-hat-developer-hub/backstage-plugin-global-header/alpha'; |
| 65 | + |
| 66 | +export const myButton = GlobalHeaderComponentBlueprint.make({ |
| 67 | + name: 'my-button', |
| 68 | + params: { |
| 69 | + icon: 'dashboard', |
| 70 | + title: 'Dashboard', |
| 71 | + link: '/dashboard', |
| 72 | + priority: 75, |
| 73 | + }, |
| 74 | +}); |
| 75 | +``` |
| 76 | + |
| 77 | +### Option B: Use building-block components |
| 78 | + |
| 79 | +For dropdowns or more control, provide a `component` that uses the exported building blocks (`GlobalHeaderIconButton`, `GlobalHeaderDropdown`). |
| 80 | + |
| 81 | +```typescript |
| 82 | +import { |
| 83 | + GlobalHeaderComponentBlueprint, |
| 84 | + GlobalHeaderDropdown, |
| 85 | +} from '@red-hat-developer-hub/backstage-plugin-global-header/alpha'; |
| 86 | + |
| 87 | +const MyDropdown = () => ( |
| 88 | + <GlobalHeaderDropdown |
| 89 | + target="my-links" |
| 90 | + isIconButton |
| 91 | + tooltip="My links" |
| 92 | + buttonContent={<MyIcon />} |
| 93 | + /> |
| 94 | +); |
| 95 | + |
| 96 | +export const myDropdown = GlobalHeaderComponentBlueprint.make({ |
| 97 | + name: 'my-dropdown', |
| 98 | + params: { component: MyDropdown, priority: 75 }, |
| 99 | +}); |
| 100 | +``` |
| 101 | + |
| 102 | +### Option C: Fully custom component |
| 103 | + |
| 104 | +Pass any React component. You own the rendering entirely. |
| 105 | + |
| 106 | +```typescript |
| 107 | +export const myWidget = GlobalHeaderComponentBlueprint.make({ |
| 108 | + name: 'my-widget', |
| 109 | + params: { |
| 110 | + component: () => <div>Anything you want</div>, |
| 111 | + priority: 60, |
| 112 | + layout: { flexGrow: 1 }, // MUI sx overrides on the wrapper |
| 113 | + }, |
| 114 | +}); |
| 115 | +``` |
| 116 | + |
| 117 | +### Parameters reference |
| 118 | + |
| 119 | +| Param | Type | Description | |
| 120 | +| ----------- | ------------------------- | ----------------------------------------------- | |
| 121 | +| `icon` | `string` | Icon name, inline SVG, or URL | |
| 122 | +| `title` | `string` | Display title (also tooltip and aria-label) | |
| 123 | +| `titleKey` | `string` | i18n translation key for the title | |
| 124 | +| `tooltip` | `string` | Explicit tooltip (overrides `title`) | |
| 125 | +| `link` | `string` | Navigation URL | |
| 126 | +| `onClick` | `() => void` | Click handler (mutually exclusive with `link`) | |
| 127 | +| `component` | `ComponentType` | Custom React component (options B/C) | |
| 128 | +| `priority` | `number` | Sort order -- higher values appear further left | |
| 129 | +| `layout` | `Record<string, unknown>` | MUI `sx` overrides on the wrapper | |
| 130 | + |
| 131 | +## Add a menu item |
| 132 | + |
| 133 | +Import `GlobalHeaderMenuItemBlueprint`. The `target` field routes the item to the right dropdown: |
| 134 | + |
| 135 | +| Target | Dropdown | |
| 136 | +| -------------- | -------------------- | |
| 137 | +| `profile` | User profile | |
| 138 | +| `help` | Help / support | |
| 139 | +| `app-launcher` | Application launcher | |
| 140 | + |
| 141 | +### Data-driven item |
| 142 | + |
| 143 | +Provide `title`, `link`, and optionally `icon` / `sectionLabel`. Items that share a `sectionLabel` are grouped under that heading. |
| 144 | + |
| 145 | +```typescript |
| 146 | +import { GlobalHeaderMenuItemBlueprint } from '@red-hat-developer-hub/backstage-plugin-global-header/alpha'; |
| 147 | + |
| 148 | +export const docsItem = GlobalHeaderMenuItemBlueprint.make({ |
| 149 | + name: 'my-docs', |
| 150 | + params: { |
| 151 | + target: 'help', |
| 152 | + title: 'API Docs', |
| 153 | + icon: 'description', |
| 154 | + link: 'https://api.example.com/docs', |
| 155 | + sectionLabel: 'Documentation', |
| 156 | + priority: 50, |
| 157 | + }, |
| 158 | +}); |
| 159 | +``` |
| 160 | + |
| 161 | +### Custom component item |
| 162 | + |
| 163 | +For full control, provide only `component` (no `title`/`link`). The dropdown renders it directly. The component receives `handleClose` and `hideDivider` as props. |
| 164 | + |
| 165 | +```typescript |
| 166 | +const MySection = ({ handleClose }: { handleClose: () => void }) => ( |
| 167 | + <div onClick={handleClose}>Custom content</div> |
| 168 | +); |
| 169 | + |
| 170 | +export const mySection = GlobalHeaderMenuItemBlueprint.make({ |
| 171 | + name: 'my-section', |
| 172 | + params: { target: 'profile', component: MySection, priority: 50 }, |
| 173 | +}); |
| 174 | +``` |
| 175 | + |
| 176 | +> If you provide `component` **together with** data fields like `title` or `link`, the item is grouped normally but your component replaces the default renderer. |
| 177 | +
|
| 178 | +### Parameters reference |
| 179 | + |
| 180 | +| Param | Type | Description | |
| 181 | +| ---------------------------------- | --------------- | ----------------------------------------------- | |
| 182 | +| `target` | `string` | **Required.** Which dropdown to add the item to | |
| 183 | +| `title` / `titleKey` | `string` | Display title / i18n key | |
| 184 | +| `subTitle` / `subTitleKey` | `string` | Secondary line / i18n key | |
| 185 | +| `icon` | `string` | Icon identifier | |
| 186 | +| `link` | `string` | Navigation URL | |
| 187 | +| `onClick` | `() => void` | Click handler | |
| 188 | +| `component` | `ComponentType` | Custom React component | |
| 189 | +| `priority` | `number` | Sort order -- higher values appear first | |
| 190 | +| `sectionLabel` | `string` | Groups items under a shared heading | |
| 191 | +| `sectionLink` / `sectionLinkLabel` | `string` | Optional link in the section heading | |
| 192 | + |
| 193 | +## Registering extensions in your plugin |
| 194 | + |
| 195 | +Include your extensions in `createFrontendPlugin`. They are automatically picked up when the plugin is added to the app. |
| 196 | + |
| 197 | +```typescript |
| 198 | +import { createFrontendPlugin } from '@backstage/frontend-plugin-api'; |
| 199 | +import { |
| 200 | + GlobalHeaderComponentBlueprint, |
| 201 | + GlobalHeaderMenuItemBlueprint, |
| 202 | +} from '@red-hat-developer-hub/backstage-plugin-global-header/alpha'; |
| 203 | + |
| 204 | +export default createFrontendPlugin({ |
| 205 | + pluginId: 'my-plugin', |
| 206 | + extensions: [ |
| 207 | + GlobalHeaderComponentBlueprint.make({ |
| 208 | + name: 'launch-button', |
| 209 | + params: { |
| 210 | + icon: 'rocket', |
| 211 | + title: 'Launch', |
| 212 | + link: '/launch', |
| 213 | + priority: 75, |
| 214 | + }, |
| 215 | + }), |
| 216 | + GlobalHeaderMenuItemBlueprint.make({ |
| 217 | + name: 'faq-link', |
| 218 | + params: { |
| 219 | + target: 'help', |
| 220 | + title: 'FAQ', |
| 221 | + link: '/faq', |
| 222 | + icon: 'help', |
| 223 | + priority: 50, |
| 224 | + }, |
| 225 | + }), |
| 226 | + ], |
| 227 | +}); |
| 228 | +``` |
| 229 | + |
| 230 | +## Add items from app-config.yaml |
| 231 | + |
| 232 | +Deployers can add toolbar buttons and dropdown menu items directly in `app-config.yaml` without writing plugin code. |
| 233 | + |
| 234 | +### Toolbar components |
| 235 | + |
| 236 | +Each entry renders an icon button in the header bar. |
| 237 | + |
| 238 | +```yaml |
| 239 | +globalHeader: |
| 240 | + components: |
| 241 | + - title: Dashboard |
| 242 | + icon: dashboard |
| 243 | + link: /dashboard |
| 244 | + tooltip: Open dashboard |
| 245 | + priority: 75 |
| 246 | +``` |
| 247 | +
|
| 248 | +`title`, `icon`, and `link` are required. Optional fields: `titleKey`, `tooltip`, `priority`. |
| 249 | + |
| 250 | +### Menu items |
| 251 | + |
| 252 | +Each entry is injected into the dropdown identified by `target`. |
| 253 | + |
| 254 | +```yaml |
| 255 | +globalHeader: |
| 256 | + menuItems: |
| 257 | + - target: app-launcher |
| 258 | + title: Internal Wiki |
| 259 | + icon: article |
| 260 | + link: https://wiki.internal.example.com |
| 261 | + sectionLabel: Resources |
| 262 | + priority: 80 |
| 263 | +``` |
| 264 | + |
| 265 | +`target`, `title`, and `link` are required. Optional fields: `titleKey`, `icon`, `sectionLabel`, `sectionLink`, `sectionLinkLabel`, `priority`. |
| 266 | + |
| 267 | +## Customize defaults |
| 268 | + |
| 269 | +Every default extension can be reconfigured, reordered, or disabled via `app-config.yaml`. Extension IDs follow the pattern `<kind>:<plugin-id>/<name>`. |
| 270 | + |
| 271 | +```yaml |
| 272 | +app: |
| 273 | + extensions: |
| 274 | + # Change the position of the search bar |
| 275 | + - gh-component:global-header/search: |
| 276 | + config: |
| 277 | + priority: 50 |
| 278 | +
|
| 279 | + # Remove notifications |
| 280 | + - gh-component:global-header/notification-button: |
| 281 | + disabled: true |
| 282 | +
|
| 283 | + # Change a menu item's title and link |
| 284 | + - gh-menu-item:global-header/app-launcher-devhub: |
| 285 | + config: |
| 286 | + title: Custom Title |
| 287 | + link: https://custom.example.com |
| 288 | +``` |
| 289 | + |
| 290 | +### Default toolbar components |
| 291 | + |
| 292 | +Extension ID pattern: `gh-component:global-header/<name>` |
| 293 | + |
| 294 | +| Name | Priority | Description | |
| 295 | +| ----------------------- | -------- | ---------------------------- | |
| 296 | +| `company-logo` | 200 | Company logo | |
| 297 | +| `search` | 100 | Search field | |
| 298 | +| `spacer` | 99 | Flexible spacer | |
| 299 | +| `self-service-button` | 90 | Create / self-service button | |
| 300 | +| `starred-dropdown` | 85 | Starred entities dropdown | |
| 301 | +| `app-launcher-dropdown` | 82 | Application launcher | |
| 302 | +| `help-dropdown` | 80 | Help / support dropdown | |
| 303 | +| `notification-button` | 70 | Notifications indicator | |
| 304 | +| `divider` | 50 | Vertical divider | |
| 305 | +| `profile-dropdown` | 10 | User profile dropdown | |
| 306 | + |
| 307 | +### Default menu items |
| 308 | + |
| 309 | +Extension ID pattern: `gh-menu-item:global-header/<name>` |
| 310 | + |
| 311 | +| Name | Target | Priority | |
| 312 | +| ------------------------- | -------------- | -------- | |
| 313 | +| `settings` | `profile` | 100 | |
| 314 | +| `my-profile` | `profile` | 90 | |
| 315 | +| `logout` | `profile` | 10 | |
| 316 | +| `support-button` | `help` | 10 | |
| 317 | +| `app-launcher-devhub` | `app-launcher` | 150 | |
| 318 | +| `app-launcher-rhdh-local` | `app-launcher` | 100 | |
| 319 | + |
| 320 | +## Advanced: building blocks and hooks |
| 321 | + |
| 322 | +For plugin authors building custom toolbar components or dropdowns, the plugin exports lower-level building blocks and React hooks: |
| 323 | + |
| 324 | +**Building-block components** (consistent styling without starting from scratch): |
| 325 | + |
| 326 | +| Component | Key props | Purpose | |
| 327 | +| ------------------------ | ------------------------- | ---------------------------------------------------------------------------- | |
| 328 | +| `GlobalHeaderIconButton` | `title`, `icon`, `to` | Icon button that navigates to a URL | |
| 329 | +| `GlobalHeaderMenuItem` | `to`, `title`, `icon` | Styled menu item link | |
| 330 | +| `GlobalHeaderDropdown` | `target`, `buttonContent` | Dropdown that auto-collects `gh-menu-item` extensions for the given `target` | |
| 331 | + |
| 332 | +**Context hooks** (direct access to collected extension data): |
| 333 | + |
| 334 | +| Hook | Returns | |
| 335 | +| ---------------------------------- | ------------------------------------------ | |
| 336 | +| `useGlobalHeaderComponents()` | All toolbar components, sorted by priority | |
| 337 | +| `useGlobalHeaderMenuItems(target)` | Menu items for a specific dropdown target | |
| 338 | + |
| 339 | +**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. |
| 340 | + |
| 341 | +All exports are available from `@red-hat-developer-hub/backstage-plugin-global-header/alpha`. |
0 commit comments