diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutBasic.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutBasic.tsx
index fd2b310d2cc..a70fed83e8b 100644
--- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutBasic.tsx
+++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutBasic.tsx
@@ -7,9 +7,9 @@ import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
import BarChartIcon from '@mui/icons-material/BarChart';
import DescriptionIcon from '@mui/icons-material/Description';
import LayersIcon from '@mui/icons-material/Layers';
-import { AppProvider, Router } from '@toolpad/core/AppProvider';
+import { AppProvider } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';
-import type { Navigation } from '@toolpad/core';
+import type { Navigation, Router } from '@toolpad/core';
const NAVIGATION: Navigation = [
{
diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutBranding.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutBranding.tsx
index d246ee44077..9ad3d2dbba2 100644
--- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutBranding.tsx
+++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutBranding.tsx
@@ -4,9 +4,9 @@ import Typography from '@mui/material/Typography';
import { extendTheme } from '@mui/material/styles';
import DashboardIcon from '@mui/icons-material/Dashboard';
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
-import { AppProvider, Router } from '@toolpad/core/AppProvider';
+import { AppProvider } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';
-import type { Navigation } from '@toolpad/core';
+import type { Navigation, Router } from '@toolpad/core';
const NAVIGATION: Navigation = [
{
diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutNavigationActions.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutNavigationActions.tsx
index e5623b8c2fc..d890368e9fb 100644
--- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutNavigationActions.tsx
+++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutNavigationActions.tsx
@@ -11,8 +11,9 @@ import CallIcon from '@mui/icons-material/Call';
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
import CallMadeIcon from '@mui/icons-material/CallMade';
import CallReceivedIcon from '@mui/icons-material/CallReceived';
-import { AppProvider, Router } from '@toolpad/core/AppProvider';
+import { AppProvider } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';
+import type { Router } from '@toolpad/core';
const demoTheme = extendTheme({
breakpoints: {
diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutNavigationItems.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutNavigationItems.tsx
index 7597bb58ef0..25524047823 100644
--- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutNavigationItems.tsx
+++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutNavigationItems.tsx
@@ -4,8 +4,9 @@ import Typography from '@mui/material/Typography';
import { extendTheme } from '@mui/material/styles';
import DescriptionIcon from '@mui/icons-material/Description';
import FolderIcon from '@mui/icons-material/Folder';
-import { AppProvider, Router } from '@toolpad/core/AppProvider';
+import { AppProvider } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';
+import type { Router } from '@toolpad/core';
const demoTheme = extendTheme({
breakpoints: {
diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutStandalone.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutStandalone.tsx
new file mode 100644
index 00000000000..d662c562142
--- /dev/null
+++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutStandalone.tsx
@@ -0,0 +1,98 @@
+import * as React from 'react';
+import Box from '@mui/material/Box';
+import Typography from '@mui/material/Typography';
+import { CssVarsProvider, extendTheme } from '@mui/material/styles';
+import DashboardIcon from '@mui/icons-material/Dashboard';
+import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
+import { DashboardLayout } from '@toolpad/core/DashboardLayout';
+import type { Navigation, Router } from '@toolpad/core';
+
+const NAVIGATION: Navigation = [
+ {
+ segment: 'dashboard',
+ title: 'Dashboard',
+ icon: ,
+ },
+ {
+ segment: 'orders',
+ title: 'Orders',
+ icon: ,
+ },
+];
+
+const demoTheme = extendTheme({
+ breakpoints: {
+ values: {
+ xs: 0,
+ sm: 600,
+ md: 600,
+ lg: 1200,
+ xl: 1536,
+ },
+ },
+});
+
+function DemoPageContent({ pathname }: { pathname: string }) {
+ return (
+
+ Dashboard content for {pathname}
+
+ );
+}
+
+interface DemoProps {
+ /**
+ * Injected by the documentation to work in an iframe.
+ * Remove this when copying and pasting into your project.
+ */
+ window?: () => Window;
+}
+
+export default function DashboardLayoutStandalone(props: DemoProps) {
+ const { window } = props;
+
+ const [pathname, setPathname] = React.useState('dashboard');
+
+ const router = React.useMemo(() => {
+ return {
+ pathname,
+ searchParams: new URLSearchParams(),
+ navigate: (path) => setPathname(String(path)),
+ };
+ }, [pathname]);
+
+ // Remove this const when copying and pasting into your project.
+ const demoWindow = window !== undefined ? window() : undefined;
+
+ return (
+
+ ,
+ title: 'MUI',
+ }}
+ colorScheme={}
+ onColorSchemeChange={}
+ router={router}
+ window={demoWindow}
+ >
+
+
+
+ );
+}
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 44f31f12474..9775ad83694 100644
--- a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md
+++ b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md
@@ -10,7 +10,8 @@ components: AppProvider, DashboardLayout
The `DashboardLayout` component is a quick, easy way to provide a standard full-screen layout with a header and sidebar to any dashboard page, as well as ready-to-use and easy to customize navigation and branding.
-Many features of this component are configurable through the [AppProvider](https://mui.com/toolpad/core/react-app-provider/) component that should wrap it.
+Many features of this component are configurable through the [AppProvider](https://mui.com/toolpad/core/react-app-provider/) component that should wrap it, which is the recommended approach for a Toolpad app.
+However, it is also possible to use the `DashboardLayout` as a standalone component by setting those configurations with the component props themselves.
## Demo
@@ -50,3 +51,9 @@ The main navigation items that can be used are:
Navigation links have an optional `action` prop that can be used to render any content on the right-side of the respective list item, such as badges with numbers, or buttons to toggle a popover menu.
{{"demo": "DashboardLayoutNavigationActions.js", "height": 500, "iframe": true}}
+
+## Standalone Usage
+
+The component branding, navigation, theme switching and routing can also be set via component props themselves instead of indirectly through an `AppProvider`.
+
+{{"demo": "DashboardLayoutStandalone.js", "height": 500, "iframe": true}}
diff --git a/docs/data/toolpad/core/introduction/TutorialDefault.tsx b/docs/data/toolpad/core/introduction/TutorialDefault.tsx
index 671c1574944..8eca598433d 100644
--- a/docs/data/toolpad/core/introduction/TutorialDefault.tsx
+++ b/docs/data/toolpad/core/introduction/TutorialDefault.tsx
@@ -1,10 +1,11 @@
import * as React from 'react';
import { extendTheme } from '@mui/material/styles';
import DashboardIcon from '@mui/icons-material/Dashboard';
-import { AppProvider, Navigation } from '@toolpad/core/AppProvider';
+import { AppProvider } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';
import { PageContainer } from '@toolpad/core/PageContainer';
import { useDemoRouter } from '@toolpad/core/internals/demo';
+import type { Navigation } from '@toolpad/core';
const NAVIGATION: Navigation = [
{
diff --git a/docs/data/toolpad/core/introduction/TutorialPages.tsx b/docs/data/toolpad/core/introduction/TutorialPages.tsx
index ab1e1daa7f8..090c4d2e0d6 100644
--- a/docs/data/toolpad/core/introduction/TutorialPages.tsx
+++ b/docs/data/toolpad/core/introduction/TutorialPages.tsx
@@ -2,10 +2,11 @@ import * as React from 'react';
import { extendTheme } from '@mui/material/styles';
import DashboardIcon from '@mui/icons-material/Dashboard';
import TimelineIcon from '@mui/icons-material/Timeline';
-import { AppProvider, Navigation } from '@toolpad/core/AppProvider';
+import { AppProvider } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';
import { useDemoRouter } from '@toolpad/core/internals/demo';
import { PageContainer } from '@toolpad/core/PageContainer';
+import type { Navigation } from '@toolpad/core';
import { Typography } from '@mui/material';
const NAVIGATION: Navigation = [
diff --git a/docs/pages/toolpad/core/api/dashboard-layout.json b/docs/pages/toolpad/core/api/dashboard-layout.json
index af4c4e66fda..f0e36989a02 100644
--- a/docs/pages/toolpad/core/api/dashboard-layout.json
+++ b/docs/pages/toolpad/core/api/dashboard-layout.json
@@ -1,5 +1,21 @@
{
- "props": { "children": { "type": { "name": "node" }, "required": true } },
+ "props": {
+ "children": { "type": { "name": "node" }, "required": true },
+ "branding": {
+ "type": { "name": "shape", "description": "{ logo?: node, title?: string }" },
+ "default": "null"
+ },
+ "colorScheme": { "type": { "name": "enum", "description": "'dark'
| 'light'" } },
+ "navigation": {
+ "type": {
+ "name": "arrayOf",
+ "description": "Array<{ action?: node, children?: Array<object
| { kind: 'header', title: string }
| { kind: 'divider' }>, icon?: node, kind?: 'page', segment: string, title?: string }
| { kind: 'header', title: string }
| { kind: 'divider' }>"
+ },
+ "default": "[]"
+ },
+ "onColorSchemeChange": { "type": { "name": "func" } },
+ "window": { "type": { "name": "object" }, "default": "window" }
+ },
"name": "DashboardLayout",
"imports": [
"import { DashboardLayout } from '@toolpad-core/DashboardLayout';",
diff --git a/docs/translations/api-docs/dashboard-layout/dashboard-layout.json b/docs/translations/api-docs/dashboard-layout/dashboard-layout.json
index 8e461ca2aaa..fa5e039d84a 100644
--- a/docs/translations/api-docs/dashboard-layout/dashboard-layout.json
+++ b/docs/translations/api-docs/dashboard-layout/dashboard-layout.json
@@ -1,5 +1,16 @@
{
"componentDescription": "",
- "propDescriptions": { "children": { "description": "The content of the dashboard." } },
+ "propDescriptions": {
+ "branding": { "description": "Branding options for the layout." },
+ "children": { "description": "The content of the dashboard." },
+ "colorScheme": { "description": "Active color scheme in theme." },
+ "navigation": { "description": "Navigation definition for the layout." },
+ "onColorSchemeChange": {
+ "description": "Callback to run when the theme color scheme is changed."
+ },
+ "window": {
+ "description": "The window where the layout is rendered. This is needed when rendering the layout inside an iframe, for example."
+ }
+ },
"classDescriptions": {}
}
diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx
index 61d1d032ead..820fd03d3fb 100644
--- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx
+++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx
@@ -1,7 +1,7 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
-import { styled, useTheme } from '@mui/material';
+import { PaletteMode, styled, useTheme } from '@mui/material';
import MuiAppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import Collapse from '@mui/material/Collapse';
@@ -34,7 +34,7 @@ import {
RouterContext,
WindowContext,
} from '../shared/context';
-import type { Navigation, NavigationPageItem } from '../AppProvider';
+import type { AppProviderProps, Navigation, NavigationPageItem } from '../AppProvider';
import { ToolpadLogo } from './ToolpadLogo';
import { getItemTitle, isPageItem } from '../shared/navigation';
import { useApplicationTitle } from '../shared/branding';
@@ -79,11 +79,20 @@ const NavigationListItemButton = styled(ListItemButton)(({ theme }) => ({
},
}));
-function ThemeSwitcher() {
+interface ThemeSwitcherProps {
+ colorScheme?: PaletteMode;
+ onColorSchemeChange?: (mode: PaletteMode) => void;
+}
+
+function ThemeSwitcher({ colorScheme, onColorSchemeChange }: ThemeSwitcherProps) {
const isSsr = useSsr();
const theme = useTheme();
- const { paletteMode, setPaletteMode, isDualTheme } = React.useContext(PaletteModeContext);
+ const paletteModeContext = React.useContext(PaletteModeContext);
+
+ const paletteMode = colorScheme ?? paletteModeContext.paletteMode;
+ const setPaletteMode = onColorSchemeChange ?? paletteModeContext.setPaletteMode;
+ const isDualTheme = !!onColorSchemeChange || paletteModeContext.isDualTheme;
const toggleMode = React.useCallback(() => {
setPaletteMode(paletteMode === 'dark' ? 'light' : 'dark');
@@ -303,6 +312,30 @@ export interface DashboardLayoutProps {
* The content of the dashboard.
*/
children: React.ReactNode;
+ /**
+ * Branding options for the layout.
+ * @default null
+ */
+ branding?: AppProviderProps['branding'];
+ /**
+ * Navigation definition for the layout.
+ * @default []
+ */
+ navigation?: AppProviderProps['navigation'];
+ /**
+ * Active color scheme in theme.
+ */
+ colorScheme?: PaletteMode;
+ /**
+ * Callback to run when the theme color scheme is changed.
+ */
+ onColorSchemeChange?: (theme: PaletteMode) => void;
+ /**
+ * The window where the layout is rendered.
+ * This is needed when rendering the layout inside an iframe, for example.
+ * @default window
+ */
+ window?: AppProviderProps['window'];
}
/**
@@ -316,13 +349,25 @@ export interface DashboardLayoutProps {
* - [DashboardLayout API](https://mui.com/toolpad/core/api/dashboard-layout)
*/
function DashboardLayout(props: DashboardLayoutProps) {
- const { children } = props;
+ const {
+ children,
+ colorScheme,
+ onColorSchemeChange,
+ branding: brandingProp,
+ navigation: navigationProp,
+ window: windowProp,
+ } = props;
+
+ const brandingContext = React.useContext(BrandingContext);
+ const navigationContext = React.useContext(NavigationContext);
+ const windowContext = React.useContext(WindowContext);
- const branding = React.useContext(BrandingContext);
- const navigation = React.useContext(NavigationContext);
- const appWindow = React.useContext(WindowContext);
const applicationTitle = useApplicationTitle();
+ const branding = brandingProp ?? brandingContext;
+ const navigation = navigationProp ?? navigationContext;
+ const appWindow = windowProp ?? windowContext;
+
const [isMobileNavigationOpen, setIsMobileNavigationOpen] = React.useState(false);
const handleSetMobileNavigationOpen = React.useCallback(
@@ -399,7 +444,7 @@ function DashboardLayout(props: DashboardLayoutProps) {
-
+