Context providers and state management system for sharing layout configuration, route information, and component state across the ProLayout ecosystem.
React Context that provides layout state and route information to all child components within the ProLayout hierarchy.
/**
* Context providing route and layout state information
*/
const RouteContext: React.Context<RouteContextType>;
interface RouteContextType {
/** Breadcrumb configuration mapping path to menu data */
breadcrumb?: Record<string, MenuDataItem>;
/** Complete menu data array */
menuData?: MenuDataItem[];
/** Mobile device detection */
isMobile?: boolean;
/** Sidebar collapse state */
collapsed?: boolean;
/** Sidebar visibility */
hasSiderMenu?: boolean;
/** Header visibility */
hasHeader?: boolean;
/** Sidebar width in pixels */
siderWidth?: number;
/** Page title information object */
pageTitleInfo?: PageTitleInfo;
/** Array of matched menu items for current route */
matchMenus?: MenuDataItem[];
/** Currently active menu item */
currentMenu?: MenuDataItem;
/** Layout mode */
layout?: 'side' | 'top' | 'mix';
/** Navigation theme */
navTheme?: 'realDark' | 'light';
/** Content width mode */
contentWidth?: 'Fluid' | 'Fixed';
/** Fixed header state */
fixedHeader?: boolean;
/** Primary color */
colorPrimary?: string;
/** Additional layout properties */
[key: string]: any;
}
interface PageTitleInfo {
/** Page title */
title?: string;
/** Page ID */
id?: string;
/** Page name */
pageName?: string;
/** Additional title info */
[key: string]: any;
}Usage Examples:
import React, { useContext } from "react";
import { RouteContext } from "@ant-design/pro-layout";
// Access layout state in components
function MyComponent() {
const {
collapsed,
isMobile,
menuData,
currentMenu,
breadcrumb
} = useContext(RouteContext);
return (
<div>
<p>Sidebar is {collapsed ? 'collapsed' : 'expanded'}</p>
<p>Device: {isMobile ? 'Mobile' : 'Desktop'}</p>
<p>Menu items: {menuData?.length || 0}</p>
<p>Current page: {currentMenu?.name || 'Unknown'}</p>
</div>
);
}
// Responsive component based on layout state
function ResponsiveComponent() {
const { isMobile, collapsed, siderWidth } = useContext(RouteContext);
const containerStyle = {
marginLeft: isMobile ? 0 : (collapsed ? 80 : siderWidth),
padding: isMobile ? 16 : 24,
};
return (
<div style={containerStyle}>
<h2>Responsive Content</h2>
<p>This component adapts to the layout state</p>
</div>
);
}
// Breadcrumb component using context
function ContextBreadcrumb() {
const { breadcrumb, matchMenus } = useContext(RouteContext);
const breadcrumbItems = matchMenus?.map(menu => ({
title: menu.name,
path: menu.path,
})) || [];
return (
<nav>
{breadcrumbItems.map((item, index) => (
<span key={index}>
{index > 0 && ' > '}
<a href={item.path}>{item.title}</a>
</span>
))}
</nav>
);
}
// Menu-aware component
function MenuAwareComponent() {
const { menuData, currentMenu } = useContext(RouteContext);
const currentSection = currentMenu?.parentKeys?.[0];
const sectionMenus = menuData?.filter(
menu => menu.key === currentSection
)?.[0]?.children || [];
return (
<div>
<h3>Section Navigation</h3>
<ul>
{sectionMenus.map(menu => (
<li key={menu.key}>
<a href={menu.path}>{menu.name}</a>
</li>
))}
</ul>
</div>
);
}RouteContext is automatically provided by ProLayout, but you can also use it independently or create custom providers.
/**
* Custom context provider for testing or standalone usage
*/
function CustomRouteProvider({ children, value }: {
children: React.ReactNode;
value: Partial<RouteContextType>;
}) {
const defaultValue: RouteContextType = {
collapsed: false,
isMobile: false,
menuData: [],
hasSiderMenu: true,
hasHeader: true,
siderWidth: 208,
layout: 'side',
navTheme: 'light',
contentWidth: 'Fluid',
...value,
};
return (
<RouteContext.Provider value={defaultValue}>
{children}
</RouteContext.Provider>
);
}import { useContext, useMemo } from "react";
import { RouteContext } from "@ant-design/pro-layout";
function useLayoutState() {
const context = useContext(RouteContext);
return useMemo(() => ({
// Layout information
isCollapsed: context.collapsed,
isMobile: context.isMobile,
layoutMode: context.layout,
theme: context.navTheme,
// Menu information
menuData: context.menuData || [],
currentMenu: context.currentMenu,
breadcrumbPath: context.matchMenus || [],
// Dimensions
siderWidth: context.siderWidth || 208,
contentWidth: context.isMobile ? '100%' :
context.collapsed ? 'calc(100% - 80px)' :
`calc(100% - ${context.siderWidth}px)`,
// Utilities
hasMenu: context.hasSiderMenu,
hasHeader: context.hasHeader,
}), [context]);
}
// Usage
function LayoutAwareComponent() {
const {
isCollapsed,
isMobile,
currentMenu,
contentWidth
} = useLayoutState();
return (
<div style={{ width: contentWidth }}>
<h1>{currentMenu?.name}</h1>
<p>Layout is {isCollapsed ? 'collapsed' : 'expanded'}</p>
</div>
);
}import { useContext, useCallback } from "react";
import { RouteContext } from "@ant-design/pro-layout";
function useMenuNavigation() {
const { menuData, breadcrumb } = useContext(RouteContext);
const findMenuByPath = useCallback((path: string) => {
const search = (menus: MenuDataItem[]): MenuDataItem | null => {
for (const menu of menus) {
if (menu.path === path) return menu;
if (menu.children) {
const found = search(menu.children);
if (found) return found;
}
}
return null;
};
return search(menuData || []);
}, [menuData]);
const getMenuPath = useCallback((menuKey: string) => {
const menu = breadcrumb?.[menuKey];
return menu?.path;
}, [breadcrumb]);
const getParentMenus = useCallback((path: string) => {
const menu = findMenuByPath(path);
return menu?.parentKeys?.map(key => breadcrumb?.[key]).filter(Boolean) || [];
}, [findMenuByPath, breadcrumb]);
return {
findMenuByPath,
getMenuPath,
getParentMenus,
menuData: menuData || [],
breadcrumb: breadcrumb || {},
};
}
// Usage
function NavigationComponent() {
const { findMenuByPath, getParentMenus } = useMenuNavigation();
const currentPath = window.location.pathname;
const currentMenu = findMenuByPath(currentPath);
const parentMenus = getParentMenus(currentPath);
return (
<div>
<h2>Current: {currentMenu?.name}</h2>
<div>
Parent path: {parentMenus.map(p => p?.name).join(' > ')}
</div>
</div>
);
}import { useContext, useEffect, useState } from "react";
import { RouteContext } from "@ant-design/pro-layout";
function useResponsiveLayout() {
const { isMobile, collapsed, siderWidth } = useContext(RouteContext);
const [screenSize, setScreenSize] = useState<'xs' | 'sm' | 'md' | 'lg' | 'xl'>('lg');
useEffect(() => {
const updateScreenSize = () => {
const width = window.innerWidth;
if (width < 576) setScreenSize('xs');
else if (width < 768) setScreenSize('sm');
else if (width < 992) setScreenSize('md');
else if (width < 1200) setScreenSize('lg');
else setScreenSize('xl');
};
updateScreenSize();
window.addEventListener('resize', updateScreenSize);
return () => window.removeEventListener('resize', updateScreenSize);
}, []);
return {
isMobile,
screenSize,
isCollapsed: collapsed,
siderWidth: siderWidth || 208,
// Computed values
contentMargin: isMobile ? 0 : (collapsed ? 80 : siderWidth),
shouldShowSider: !isMobile || !collapsed,
breakpoint: {
xs: screenSize === 'xs',
sm: screenSize === 'sm',
md: screenSize === 'md',
lg: screenSize === 'lg',
xl: screenSize === 'xl',
},
};
}
// Usage
function ResponsiveGrid() {
const { screenSize, contentMargin, breakpoint } = useResponsiveLayout();
const gridCols = breakpoint.xs ? 1 : breakpoint.sm ? 2 : breakpoint.md ? 3 : 4;
return (
<div style={{
marginLeft: contentMargin,
display: 'grid',
gridTemplateColumns: `repeat(${gridCols}, 1fr)`,
gap: 16,
}}>
{/* Grid items */}
</div>
);
}import { ComponentType } from "react";
import { RouteContext, RouteContextType } from "@ant-design/pro-layout";
function withRouteContext<P extends object>(
Component: ComponentType<P & { routeContext: RouteContextType }>
) {
return function WrappedComponent(props: P) {
return (
<RouteContext.Consumer>
{(routeContext) => (
<Component {...props} routeContext={routeContext} />
)}
</RouteContext.Consumer>
);
};
}
// Usage
interface MyComponentProps {
title: string;
routeContext: RouteContextType;
}
const MyComponent = withRouteContext<{ title: string }>(
({ title, routeContext }: MyComponentProps) => (
<div>
<h1>{title}</h1>
<p>Mobile: {routeContext.isMobile ? 'Yes' : 'No'}</p>
</div>
)
);import { render } from "@testing-library/react";
import { RouteContext, RouteContextType } from "@ant-design/pro-layout";
function renderWithContext(
component: React.ReactElement,
contextValue: Partial<RouteContextType> = {}
) {
const defaultValue: RouteContextType = {
collapsed: false,
isMobile: false,
menuData: [],
hasSiderMenu: true,
hasHeader: true,
siderWidth: 208,
...contextValue,
};
return render(
<RouteContext.Provider value={defaultValue}>
{component}
</RouteContext.Provider>
);
}
// Usage in tests
test('component renders correctly on mobile', () => {
const { getByText } = renderWithContext(
<MyComponent />,
{ isMobile: true, collapsed: true }
);
expect(getByText('Mobile view')).toBeInTheDocument();
});