diff --git a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md
index dc46918b677..056add3b03c 100644
--- a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md
+++ b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md
@@ -37,6 +37,8 @@ This can be done via the `branding` prop in the [AppProvider](https://mui.com/to
{{"demo": "DashboardLayoutBranding.js", "height": 400, "iframe": true}}
:::info
+Optionally, a specific `branding` can also be passed as a prop to the `DashboardLayout` component itself, which properties take precedence over any properties of the `branding` prop passed to the `AppProvider`.
+
You may also override the default branding by passing in your own component to the `appTitle` slot, as shown in the [Slots section](#slots).
:::
@@ -46,6 +48,10 @@ The `navigation` prop in the [AppProvider](https://mui.com/toolpad/core/react-ap
The flexibility in composing and ordering these different elements allows for a great variety of navigation structures to fit your use case.
+:::info
+Optionally, a specific `navigation` can also be passed as a prop to the `DashboardLayout` component itself, which takes complete precedence over any `navigation` prop passed to the `AppProvider`.
+:::
+
### Navigation links
Navigation links can be placed in the sidebar as items with the format:
diff --git a/docs/pages/toolpad/core/api/dashboard-layout.json b/docs/pages/toolpad/core/api/dashboard-layout.json
index fb900ae907a..6cb95c85aa3 100644
--- a/docs/pages/toolpad/core/api/dashboard-layout.json
+++ b/docs/pages/toolpad/core/api/dashboard-layout.json
@@ -11,6 +11,13 @@
"defaultSidebarCollapsed": { "type": { "name": "bool" }, "default": "false" },
"disableCollapsibleSidebar": { "type": { "name": "bool" }, "default": "false" },
"hideNavigation": { "type": { "name": "bool" }, "default": "false" },
+ "navigation": {
+ "type": {
+ "name": "arrayOf",
+ "description": "Array<{ action?: node, children?: Array<object
| { kind: 'header', title: string }
| { kind: 'divider' }>, icon?: node, kind?: 'page', pattern?: string, segment?: string, title?: string }
| { kind: 'header', title: string }
| { kind: 'divider' }>"
+ },
+ "default": "[]"
+ },
"sidebarExpandedWidth": {
"type": { "name": "union", "description": "number
| string" },
"default": "320"
diff --git a/docs/translations/api-docs/dashboard-layout/dashboard-layout.json b/docs/translations/api-docs/dashboard-layout/dashboard-layout.json
index 3cd8c58bad6..a9668478927 100644
--- a/docs/translations/api-docs/dashboard-layout/dashboard-layout.json
+++ b/docs/translations/api-docs/dashboard-layout/dashboard-layout.json
@@ -12,6 +12,7 @@
"hideNavigation": {
"description": "Whether the navigation bar and menu icon should be hidden"
},
+ "navigation": { "description": "Navigation definition for the dashboard." },
"sidebarExpandedWidth": { "description": "Width of the sidebar when expanded." },
"slotProps": { "description": "The props used for each slot inside." },
"slots": { "description": "The components used for each slot inside." },
diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx
index da545108989..f312b2c9c9b 100644
--- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx
+++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx
@@ -19,7 +19,7 @@ import { DashboardSidebarSubNavigation } from './DashboardSidebarSubNavigation';
import { ToolbarActions } from './ToolbarActions';
import { AppTitle, AppTitleProps } from './AppTitle';
import { getDrawerSxTransitionMixin, getDrawerWidthTransitionMixin } from './utils';
-import type { Branding } from '../AppProvider';
+import type { Branding, Navigation } from '../AppProvider';
const AppBar = styled(MuiAppBar)(({ theme }) => ({
borderWidth: 0,
@@ -74,6 +74,11 @@ export interface DashboardLayoutProps {
* @default null
*/
branding?: Branding | null;
+ /**
+ * Navigation definition for the dashboard.
+ * @default []
+ */
+ navigation?: Navigation;
/**
* Whether the sidebar should not be collapsible to a mini variant in desktop and tablet viewports.
* @default false
@@ -124,6 +129,7 @@ function DashboardLayout(props: DashboardLayoutProps) {
const {
children,
branding: brandingProp,
+ navigation: navigationProp,
disableCollapsibleSidebar = false,
defaultSidebarCollapsed = false,
hideNavigation = false,
@@ -139,6 +145,9 @@ function DashboardLayout(props: DashboardLayoutProps) {
const navigationContext = React.useContext(NavigationContext);
const appWindowContext = React.useContext(WindowContext);
+ const branding = { ...brandingContext, ...brandingProp };
+ const navigation = navigationProp ?? navigationContext;
+
const [isDesktopNavigationExpanded, setIsDesktopNavigationExpanded] =
React.useState(!defaultSidebarCollapsed);
const [isMobileNavigationExpanded, setIsMobileNavigationExpanded] = React.useState(false);
@@ -208,10 +217,10 @@ function DashboardLayout(props: DashboardLayoutProps) {
// If useEffect was used, the reset would also happen on the client render after SSR which we don't need
React.useMemo(() => {
- if (navigationContext) {
+ if (navigation) {
selectedItemIdRef.current = '';
}
- }, [navigationContext]);
+ }, [navigation]);
const isDesktopMini = !disableCollapsibleSidebar && !isDesktopNavigationExpanded;
const isMobileMini = !disableCollapsibleSidebar && !isMobileNavigationExpanded;
@@ -247,8 +256,6 @@ function DashboardLayout(props: DashboardLayoutProps) {
const ToolbarAccountSlot = slots?.toolbarAccount ?? Account;
const SidebarFooterSlot = slots?.sidebarFooter ?? null;
- const appTitleBrandingProp = { ...brandingContext, ...brandingProp };
-
const getDrawerContent = React.useCallback(
(isMini: boolean, viewport: 'phone' | 'tablet' | 'desktop') => (
@@ -262,14 +269,14 @@ function DashboardLayout(props: DashboardLayoutProps) {
flexDirection: 'column',
justifyContent: 'space-between',
overflow: 'auto',
- pt: navigationContext[0]?.kind === 'header' && !isMini ? 0 : 2,
+ pt: navigation[0]?.kind === 'header' && !isMini ? 0 : 2,
...(hasDrawerTransitions
? getDrawerSxTransitionMixin(isNavigationFullyExpanded, 'padding')
: {}),
}}
>
+
)}
@@ -483,6 +490,41 @@ DashboardLayout.propTypes /* remove-proptypes */ = {
* @default false
*/
hideNavigation: PropTypes.bool,
+ /**
+ * Navigation definition for the dashboard.
+ * @default []
+ */
+ navigation: PropTypes.arrayOf(
+ PropTypes.oneOfType([
+ PropTypes.shape({
+ action: PropTypes.node,
+ children: PropTypes.arrayOf(
+ PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.shape({
+ kind: PropTypes.oneOf(['header']).isRequired,
+ title: PropTypes.string.isRequired,
+ }),
+ PropTypes.shape({
+ kind: PropTypes.oneOf(['divider']).isRequired,
+ }),
+ ]).isRequired,
+ ),
+ icon: PropTypes.node,
+ kind: PropTypes.oneOf(['page']),
+ pattern: PropTypes.string,
+ segment: PropTypes.string,
+ title: PropTypes.string,
+ }),
+ PropTypes.shape({
+ kind: PropTypes.oneOf(['header']).isRequired,
+ title: PropTypes.string.isRequired,
+ }),
+ PropTypes.shape({
+ kind: PropTypes.oneOf(['divider']).isRequired,
+ }),
+ ]).isRequired,
+ ),
/**
* Width of the sidebar when expanded.
* @default 320