Configure navigation
Navigation is an array on ShellUIConfig. Each item loads an iframe URL (or a built-in shell route) at a unique path. Groups add section titles. The same items feed sidebar, app bar, floating tabs, and the windows start menu - visibility depends on layout.
Item fields
import type { ShellUIConfig } from '@shellui/core';
const config: ShellUIConfig = {
navigation: [
{
label: 'Home',
path: 'home',
url: 'http://localhost:4000/',
icon: '/icons/home.svg',
},
{
label: 'About',
path: 'about',
url: 'https://example.com/about',
},
],
};
label(string | LocalizedString, required): display textpath(string, required): unique id used in the shell URL (for example/home)url(string, required): iframe or route URLicon(string, optional): SVG path such as/icons/home.svghidden: hide from sidebar and 404 links; the route still existshideWhenLoggedOut: hide while signed outrequiresAuth: redirect signed-out visitors to/login?next=...requiresDevMode: only when Settings → Advanced → Developer features is onrequiresStaff: only staff (isStaff). The shell route guard blocks signed-out and non-staff users with an access-forbidden view; backend APIs must still enforce authorization.hiddenOnMobile/hiddenOnDesktop: hide from the mobile sheet or desktop sidebar (ignored ifhiddenis true)openIn:'default' | 'modal' | 'drawer' | 'external'drawerPosition:'top' | 'bottom' | 'left' | 'right'whenopenIn: 'drawer'position:'start'(main) or'end'(footer / icon end of app bar)settings: URL of a panel under Settings → Applications - see Application settingsuseHashRouter: whentrue, treat as hash routing. If omitted, inferred fromurlcontaining/#/safeForAuthToken: set totrueto opt in to sharing the session access token (settings.accessToken) with that iframe. Default is not shared (undefined/false). The refresh token is never shared with companions.
Hash URL navigation
Apps that use hash routing (React Router HashRouter, Vue hash mode) need the literal sequence /#/ in the item url and in links the shell opens.
- Config:
http://localhost:5173/#/orhttp://localhost:5173/#/themesis treated as a hash-router app. You can also setuseHashRouter: true. - Incoming URLs: the shell applies hash logic only if the URL contains
/#/. The part after/#/becomes the path inside the iframe. The shell's own URL stays path-based.
navigation: [
{
label: 'Themes',
path: 'themes',
url: 'http://localhost:5173/#/themes',
},
{
label: 'Home',
path: 'home',
url: 'http://localhost:5173/#/',
},
];
Opening http://localhost:5173/#/themes/foo matches the Themes item and passes subpath foo to the iframe. URLs without /#/ use pathname + search only.
Groups
navigation: [
{
label: 'Dashboard',
path: 'dashboard',
url: 'http://localhost:4000/',
},
{
title: 'System',
items: [
{
label: 'Settings',
path: 'settings',
url: 'http://localhost:4000/settings',
icon: '/icons/settings.svg',
},
],
},
];
title may be a string or localized object. Groups also accept position: 'end'.
Localized labels
When language includes more than one code, label and group title can be objects. See Internationalization.
label: {
en: "Documentation",
fr: "Documentation",
}
A plain string stays the same in every language.
Visibility and protection
{
"label": "Admin Panel",
"path": "admin",
"url": "/admin",
"hidden": true
}
{
"label": "Desktop Only",
"path": "desktop",
"url": "/desktop",
"hiddenOnMobile": true
}
{
"label": "Billing",
"path": "billing",
"url": "https://app.example.com/billing",
"hideWhenLoggedOut": true,
"requiresAuth": true
}
If a signed-out visitor opens /billing with requiresAuth, the shell redirects to /login?next=%2Fbilling and returns after login. Logout from a requiresAuth route goes to / first. Details: Authentication.
Opening modes
default: main content iframemodal: overlay (desktop dialog / mobile sheet) - see Modals and drawersdrawer: edge panel;drawerPositiondefaults to'right'external: new tab (target="_blank")
Sidebar position
position: 'start' (default) is the main list. position: 'end' is the sidebar footer (or icon-only end of the app bar). Apply position on items or on groups.