Discover how to implement a professional-grade dynamic menu in React that your users will love
The Art of Creating Perfect Dynamic Menus
Imagine a critical hospital system where doctors constantly switch between different sections during an emergency. Every second counts, and the last thing they need is to waste time navigating through a confusing menu or having to reopen multiple sections every time they refresh the page. This is where our revolutionary solution comes into play.
In this article, you'll discover how to create a dynamic menu in React that not only maintains its state perfectly between sessions but does so without relying on LocalStorage - an approach that elegantly combines security and performance.
The Real Challenge
In modern enterprise applications, especially in critical environments like hospital systems, banking, or real-time systems, user experience cannot be compromised. Some specific challenges include:
- Maintaining consistent navigation states between refreshes
- Ensuring performance even with complex menu structures
- Implementing a solution that scales with application growth
- Maintaining security without relying on local storage
The Solution: Intelligent Data Structure
Our approach utilizes a tree data structure combined with efficient search algorithms. Let's dive into the technical details that make this possible.
Menu Tree Architecture
The foundation of our implementation is a carefully designed tree data structure. Each node represents a menu item that can contain submenus, creating a natural and intuitive hierarchy.
export enum MenuType {
PRIMARY = 'PRIMARY',
SECONDARY = 'SECONDARY',
TERTIARY = 'TERTIARY'
}
export interface TreeNode {
key: string;
label: string;
icon?: ReactNode;
path?: string;
children?: TreeNode[];
type: MenuType;
}
export const menuTree: TreeNode[] = [
{
key: "/",
label: "Dashboard",
icon: <PieChartOutlined />,
path: "/",
type: MenuType.PRIMARY,
},
{
key: "/patients",
label: "Patient Management",
icon: <TeamOutlined />,
type: MenuType.PRIMARY,
children: [
{
key: "/patients/list",
label: "Patient List",
path: "/patients/list",
type: MenuType.SECONDARY,
},
{
key: "/patients/admissions",
label: "Admissions",
path: "/patients/admissions",
type: MenuType.SECONDARY,
}
]
},
// Additional menu items...
];
Efficient Indexing System
One of the keys to our solution's exceptional performance is the indexing system:
export const createMenuIndex = (tree: TreeNode[]): Map<string, TreeNode> => {
const index = new Map<string, TreeNode>();
const traverse = (nodes: TreeNode[]) => {
nodes.forEach(node => {
if (node.path) {
index.set(node.path, node);
}
if (node.children) {
traverse(node.children);
}
});
};
traverse(tree);
return index;
};
Intelligent Navigation Management
The true differentiator of our implementation lies in how we manage navigation and menu state:
export function findParentKeys({
tree,
targetKey,
parentKeys = []
}: {
tree: TreeNode[];
targetKey: string;
parentKeys?: string[];
}): string[] {
for (const node of tree) {
if (node.key === targetKey) {
return parentKeys;
}
if (node.children) {
const found = findParentKeys({
tree: node.children,
targetKey,
parentKeys: [...parentKeys, node.key]
});
if (found.length) return found;
}
}
return [];
}
Optimized React Component
The final component elegantly integrates all these functionalities:
import React, { useState, useEffect, useMemo } from 'react';
import { Layout, Menu } from 'antd';
import { useNavigate, useLocation } from 'react-router-dom';
import type { MenuProps } from 'antd';
import { PieChartOutlined, MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
const { Sider } = Layout;
type MenuClickEventHandler = MenuProps['onClick'];
const MenuComponent: React.FC = () => {
const [currentMenu, setCurrentMenu] = useState<MenuType>(MenuType.PRIMARY);
const [currentSelectedMenuKey, setCurrentSelectedMenuKey] = useState('');
const [isMenuCollapsed, setIsMenuCollapsed] = useState(false);
const [openKeys, setOpenKeys] = useState<string[]>([]);
const navigate = useNavigate();
const location = useLocation();
const menuIndex = useMemo(() => createMenuIndex(menuTree), []);
useEffect(() => {
const foundNode = menuIndex.get(location.pathname);
if (foundNode) {
setCurrentMenu(foundNode.type);
setCurrentSelectedMenuKey(foundNode.key);
const parentKeys = findParentKeys({
targetKey: foundNode.key,
tree: menuTree
});
setOpenKeys(previous => Array.from(new Set([...previous, ...parentKeys])));
}
}, [location.pathname, menuIndex]);
const handleOnMenuItemClick: MenuClickEventHandler = ({ key }) => {
const foundNode = menuIndex.get(key);
if (foundNode?.path) {
navigate(foundNode.path);
}
};
const toggleCollapsed = () => {
setIsMenuCollapsed(!isMenuCollapsed);
};
const menuItems = useMemo(() => {
const convertTreeToAntdItems = (nodes: TreeNode[]): MenuProps['items'] =>
nodes.map(node => ({
key: node.path || node.key,
icon: node.icon,
label: node.label,
children: node.children ? convertTreeToAntdItems(node.children) : undefined,
}));
return convertTreeToAntdItems(menuTree);
}, []);
return (
<Sider
width={isMenuCollapsed ? 100 : 280}
style={{
boxShadow: '2px 4px 6px -1px rgba(0, 0, 0, 0.1)',
minHeight: '100vh'
}}
>
<Menu
onClick={handleOnMenuItemClick}
items={menuItems}
mode="inline"
selectedKeys={[currentSelectedMenuKey]}
inlineCollapsed={isMenuCollapsed}
openKeys={openKeys}
onOpenChange={setOpenKeys}
style={{ height: '100%', borderRight: 0 }}
/>
<div
onClick={toggleCollapsed}
style={{
padding: '16px',
textAlign: 'center',
cursor: 'pointer',
borderTop: '1px solid #f0f0f0'
}}
>
{isMenuCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</div>
</Sider>
);
};
export default MenuComponent;
Results and Benefits
Our implementation brings tangible benefits:
- Exceptional Performance: Instant navigation even in complex menus
- Enhanced Security: No dependency on local storage
- Superior User Experience: Persistent states without compromises
- Maintainability: Clean and well-structured code
- Scalability: Ready to grow with your application
Implementation Steps
To implement this solution in your project:
- Install the required dependencies:
npm install antd @ant-design/icons react-router-dom
- Copy the menu component and supporting functions
- Set up your routing structure
- Configure your menu tree structure
- Integrate the component into your layout
- Customize the styling to match your brand
Beyond the Basics
To take this implementation further, consider:
- Adding keyboard navigation support
- Implementing menu search functionality
- Adding animation for state transitions
- Creating a context for global menu state management
- Adding accessibility features (ARIA labels, keyboard shortcuts)
Conclusion
Implementing dynamic menus in React doesn't have to be challenging. With the approach presented, you have a robust, performant, and secure solution that will elevate your application's quality to a new level.
Try this implementation in your next project and discover why leading enterprises are adopting this approach for their critical systems.
Top comments (0)