
Syncfusion React Context Menu
- 349 installs
- 3 repo stars
- Updated July 28, 2026
- syncfusion/react-ui-components-skills
Use syncfusion-react-context-menu for development tasks
About
syncfusion-react-context-menu: A skill for development. This provides functionality for development workflows.
- syncfusion-react-context-menu
Syncfusion React Context Menu by the numbers
- 349 all-time installs (skills.sh)
- +52 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,203 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/react-ui-components-skills --skill syncfusion-react-context-menuAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 349 |
|---|---|
| repo stars | ★ 3 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/react-ui-components-skills ↗ |
What it does
Use syncfusion-react-context-menu for development tasks
Files
Implementing Syncfusion React Context Menu
Complete API Reference & Feature Guide
When to Use This Skill
ALWAYS use this skill when users need to:
- Install and configure Syncfusion React ContextMenu component
- Create and manage menu items (text, icons, separators, nested menus, URLs)
- Programmatically control menus using 15+ methods
- Handle all 7 events with their respective event arguments
- Customize appearance with CSS classes, themes, templates, animations
- Bind data from local sources or dynamic data structures
- Implement accessibility (keyboard navigation, ARIA, RTL, WCAG 2.2)
- Manage dynamic menus (add, remove, show, hide, enable/disable)
- Create context-specific actions (files, editors, workflows)
---
Getting Started
📄 Read: references/getting-started.md
- Package installation (
@syncfusion/ej2-react-navigations) - Development environment setup (Vite, Create React App)
- CSS stylesheet imports and theme configuration
- Basic ContextMenu implementation
- Running and testing the application
---
Component Properties Reference
The ContextMenuComponent accepts 18+ configuration properties to control behavior, appearance, and interaction:
Essential Properties
target (Required)
Type: string
CSS selector for the element that triggers the context menu (right-click or touch-hold).
<ContextMenuComponent target="#myElement" items={menuItems} />Example: Trigger on specific element
function App() {
return (
<div>
<div id="target">Right-click here</div>
<ContextMenuComponent target="#target" items={menuItems} />
</div>
);
}---
items (Required)
Type: MenuItemModel[]
Array of menu items to display. Each item can have text, icons, nested items, separators, etc.
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' },
{ separator: true },
{
text: 'More',
items: [
{ text: 'Properties' },
{ text: 'Delete' }
]
}
];
<ContextMenuComponent target="#target" items={menuItems} />---
Display & Animation Properties
animationSettings
Type: MenuAnimationSettingsModel
Configure menu opening/closing animation effects.
Properties:
effect: Animation type (None, SlideDown, ZoomIn, FadeIn)duration: Animation time in milliseconds (default: 400)easing: CSS easing function (default: ease)
const animationSettings = {
effect: 'FadeIn',
duration: 300,
easing: 'ease-out'
};
<ContextMenuComponent
target="#target"
items={menuItems}
animationSettings={animationSettings}
/>Available Effects:
| Effect | Description |
|---|---|
None | No animation |
SlideDown | Slide down from top |
ZoomIn | Zoom in effect |
FadeIn | Fade in opacity |
---
cssClass
Type: string
Add custom CSS classes to the ContextMenu wrapper for custom styling.
<ContextMenuComponent
target="#target"
items={menuItems}
cssClass="custom-menu dark-theme"
/>---
enableScrolling
Type: boolean (default: false)
Enable scrolling when menu height exceeds available space.
<ContextMenuComponent
target="#target"
items={largeMenuList}
enableScrolling={true}
/>---
Interaction Properties
filter
Type: string
CSS selector for specific elements inside the target that should trigger the context menu. Use to limit context menu to certain child elements.
// Context menu only appears on table rows, not the entire table
<ContextMenuComponent
target="#table"
filter="tr"
items={menuItems}
/>---
hoverDelay
Type: number (default: 400)
Milliseconds to wait before displaying submenu on hover.
<ContextMenuComponent
target="#target"
items={menuItems}
hoverDelay={500} // 500ms before submenu appears
/>---
showItemOnClick
Type: boolean (default: false)
Force submenus to open only on click (not on hover). When true, arrow key navigation is required to open submenus.
<ContextMenuComponent
target="#target"
items={menuItems}
showItemOnClick={true} // Submenus open on click only
/>---
Data & Content Properties
itemTemplate
Type: string | Function
Custom HTML template for menu items. Use template string with property placeholders (${propertyName}).
const template = `
<div class="menu-item">
<span class="${iconCss}"></span>
<span>${text}</span>
<span class="shortcut">${shortcut}</span>
</div>
`;
<ContextMenuComponent
target="#target"
items={menuItems}
itemTemplate={template}
/>---
locale
Type: string (default: 'en-US')
Set localization language for component. Overrides global culture setting.
<ContextMenuComponent
target="#target"
items={menuItems}
locale="es-ES" // Spanish localization
/>---
Security & State Properties
enableHtmlSanitizer
Type: boolean (default: true)
Enable HTML sanitization to prevent XSS attacks. Sanitizes untrusted HTML in menu items.
<ContextMenuComponent
target="#target"
items={menuItems}
enableHtmlSanitizer={true} // Sanitize HTML content
/>---
enablePersistence
Type: boolean (default: false)
Persist component state (expanded/collapsed state) across page reloads using browser storage.
<ContextMenuComponent
target="#target"
items={menuItems}
enablePersistence={true}
/>---
enableRtl
Type: boolean (default: false)
Enable right-to-left (RTL) layout for Arabic, Hebrew, and other RTL languages.
<ContextMenuComponent
target="#target"
items={menuItems}
enableRtl={true}
/>---
Menu Item Model Properties
Each menu item is configured using MenuItemModel interface with the following properties:
Text & Display
text
Type: string
Display text for the menu item.
{ text: 'Cut' }
{ text: 'Copy' }---
id
Type: string
Unique identifier for the menu item. Use for identifying items in event handlers or programmatic operations.
{ id: 'cut-item', text: 'Cut' }
{ id: 'copy-item', text: 'Copy' }---
iconCss
Type: string
CSS class for icon display. Supports Syncfusion icons or custom icon classes.
{ text: 'Cut', iconCss: 'e-icons e-cut' }
{ text: 'Copy', iconCss: 'e-icons e-copy' }
{ text: 'Delete', iconCss: 'e-icons e-delete' }---
Item Structure
items
Type: MenuItemModel[]
Nested submenu items. Creates hierarchical menu structure.
{
text: 'File',
items: [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' }
]
}---
separator
Type: boolean (default: false)
Render as a visual separator line instead of a clickable item.
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ separator: true }, // Visual divider
{ text: 'Delete' }
];---
Navigation
url
Type: string
Navigation URL. Creates an anchor link that navigates when clicked.
{ text: 'Visit Site', url: 'https://example.com' }
{ text: 'Documentation', url: '/docs' }---
Custom Attributes
htmlAttributes
Type: Record<string, string>
Add custom HTML attributes to menu item element.
{
text: 'Download',
htmlAttributes: {
'data-action': 'download',
'aria-label': 'Download file',
'title': 'Download the file'
}
}---
Menu Methods – Programmatic Control
The ContextMenu exposes 15+ methods for runtime control and manipulation:
Menu State Control
open(top: number, left: number, target?: HTMLElement): void
Programmatically open the context menu at specified coordinates.
Parameters:
top: Vertical position (Y-coordinate in pixels)left: Horizontal position (X-coordinate in pixels)target: Optional HTML element for z-index calculation
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const openMenuAtClick = (event: React.MouseEvent) => {
menuRef.current?.open(event.clientY, event.clientX);
};
return (
<div>
<button onClick={openMenuAtClick}>Open Menu</button>
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
</div>
);
}---
close(): void
Programmatically close the context menu.
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const closeMenu = () => {
menuRef.current?.close();
};
const handleItemSelect = (args: MenuEventArgs) => {
console.log('Selected:', args.item?.text);
closeMenu(); // Close menu after selection
};
return (
<ContextMenuComponent
ref={menuRef}
target="#target"
items={menuItems}
select={handleItemSelect}
/>
);
}---
Item Visibility Control
showItems(items: string[], isUniqueId?: boolean): void
Show menu items that were previously hidden.
Parameters:
items: Array of item text or IDs to showisUniqueId: If true, treat items as unique IDs; if false, treat as text (default: false)
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const showDeleteOption = () => {
menuRef.current?.showItems(['Delete', 'Rename']);
};
return (
<div>
<button onClick={showDeleteOption}>Show Options</button>
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
</div>
);
}---
hideItems(items: string[], isUniqueId?: boolean): void
Hide specific menu items from display while keeping them in the menu structure.
Parameters:
items: Array of item text or IDs to hideisUniqueId: If true, treat items as unique IDs
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
React.useEffect(() => {
// Hide delete option for read-only mode
if (isReadOnly) {
menuRef.current?.hideItems(['Delete', 'Modify']);
}
}, [isReadOnly]);
return (
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
);
}---
Item State Management
enableItems(items: string[], enable: boolean, isUniqueId?: boolean): void
Enable or disable menu items to control interactivity.
Parameters:
items: Array of item text or IDs to modifyenable: true to enable, false to disableisUniqueId: If true, treat items as unique IDs
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const disablePasteIfNoClipboard = async () => {
const canPaste = await navigator.permissions.query({ name: 'clipboard-read' });
if (canPaste.state === 'denied') {
menuRef.current?.enableItems(['Paste'], false);
}
};
React.useEffect(() => {
disablePasteIfNoClipboard();
}, []);
return (
<ContextMenuComponent
ref={menuRef}
target="#target"
items={menuItems}
beforeOpen={disablePasteIfNoClipboard}
/>
);
}---
Item Manipulation
insertAfter(items: MenuItemModel[], text: string, isUniqueId?: boolean): void
Insert new menu items after a specified target item.
Parameters:
items: Array of new MenuItemModel items to inserttext: Text or ID of target item to insert afterisUniqueId: If true, treat text as unique ID
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const addSortByOption = () => {
const newItems: MenuItemModel[] = [
{ text: 'Sort By Name' },
{ text: 'Sort By Date' }
];
menuRef.current?.insertAfter(newItems, 'View');
};
return (
<div>
<button onClick={addSortByOption}>Add Sort Options</button>
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
</div>
);
}---
insertBefore(items: MenuItemModel[], text: string, isUniqueId?: boolean): void
Insert new menu items before a specified target item.
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const addPremiumOptions = () => {
const premiumItems: MenuItemModel[] = [
{ text: 'Premium Feature 1' },
{ text: 'Premium Feature 2', iconCss: 'e-icons e-star' }
];
// Insert before 'Delete' option
menuRef.current?.insertBefore(premiumItems, 'Delete');
};
React.useEffect(() => {
if (user.isPremium) {
addPremiumOptions();
}
}, [user.isPremium]);
return (
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
);
}---
removeItems(items: string[], isUniqueId?: boolean): void
Remove menu items from the menu.
Parameters:
items: Array of item text or IDs to removeisUniqueId: If true, treat items as unique IDs
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const removeRestrictedActions = () => {
menuRef.current?.removeItems(['Delete', 'Export', 'Archive']);
};
React.useEffect(() => {
if (userRole === 'viewer') {
removeRestrictedActions();
}
}, [userRole]);
return (
<ContextMenuComponent
ref={menuRef}
target="#target"
items={menuItems}
created={removeRestrictedActions}
/>
);
}---
Item Queries
getItemIndex(item: MenuItem | string, isUniqueId?: boolean): number[]
Get the index/indices of a menu item. Returns array because item can exist at multiple levels (for nested items).
Parameters:
item: MenuItem object or text/ID string to findisUniqueId: If true, treat item as unique ID
Returns: number[] - Array of index numbers representing item position
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const findItemPosition = () => {
const indices = menuRef.current?.getItemIndex('Copy');
console.log('Copy is at index:', indices); // Output: [1] or [0, 1] for nested
const nestedIndices = menuRef.current?.getItemIndex('Open', false);
console.log('Open is at indices:', nestedIndices); // Output: [0, 1] for nested
};
return (
<div>
<button onClick={findItemPosition}>Find Item</button>
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
</div>
);
}---
setItem(item: MenuItem, id?: string, isUniqueId?: boolean): void
Update an existing menu item's properties.
Parameters:
item: MenuItem object with updated propertiesid: Text or ID of item to updateisUniqueId: If true, treat id as unique ID
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const updateDeleteItemStyle = () => {
const updatedItem: MenuItemModel = {
text: 'Delete',
iconCss: 'e-icons e-delete',
htmlAttributes: {
'class': 'dangerous-action'
}
};
menuRef.current?.setItem(updatedItem, 'Delete');
};
return (
<div>
<button onClick={updateDeleteItemStyle}>Update Delete Item</button>
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
</div>
);
}---
Component Lifecycle
destroy(): void
Destroy the ContextMenu component and free resources.
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
React.useEffect(() => {
return () => {
// Cleanup on component unmount
menuRef.current?.destroy();
};
}, []);
return (
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
);
}---
Events & Event Arguments
The ContextMenu component provides 7 events for monitoring and controlling user interactions:
Menu Lifecycle Events
beforeOpen
Type: EmitType<BeforeOpenCloseMenuEventArgs>
Fires before the menu opens. Use to prevent opening, modify items, or prepare data.
Event Arguments:
interface BeforeOpenCloseMenuEventArgs {
element: HTMLElement; // Menu element
event: Event; // Browser event object
items: MenuItemModel[]; // Current menu items
cancel: boolean; // Set to true to prevent action
parentItem?: MenuItemModel; // Parent item if submenu
}Example: Prevent opening in certain conditions
function App() {
const handleBeforeOpen = (args: BeforeOpenCloseMenuEventArgs) => {
// Prevent opening on read-only elements
const target = args.event?.target as HTMLElement;
if (target?.classList.contains('read-only')) {
args.cancel = true; // Prevent menu from opening
return;
}
// Modify items before opening
const selectedText = window.getSelection()?.toString();
if (!selectedText) {
// Disable text-related operations if no text selected
args.items = args.items?.map(item => ({
...item,
disabled: ['Copy', 'Cut'].includes(item.text as string)
}));
}
};
return (
<ContextMenuComponent
target="#target"
items={menuItems}
beforeOpen={handleBeforeOpen}
/>
);
}---
onOpen
Type: EmitType<OpenCloseMenuEventArgs>
Fires after the menu opens. Use to initialize UI or perform post-open actions.
Event Arguments:
interface OpenCloseMenuEventArgs {
element: HTMLElement; // Menu element
event: Event; // Browser event that triggered opening
items: MenuItemModel[]; // Current menu items
}Example: Initialize after opening
function App() {
const handleOnOpen = (args: OpenCloseMenuEventArgs) => {
console.log('Menu opened at:', new Date());
console.log('Number of items:', args.items?.length);
// Set focus to first menu item for accessibility
setTimeout(() => {
const firstItem = args.element?.querySelector('.e-menu-item');
(firstItem as HTMLElement)?.focus();
}, 0);
};
return (
<ContextMenuComponent
target="#target"
items={menuItems}
onOpen={handleOnOpen}
/>
);
}---
beforeClose
Type: EmitType<BeforeOpenCloseMenuEventArgs>
Fires before the menu closes. Use to prevent closing or save state.
Example: Prevent closing on unsaved changes
function App() {
const [hasUnsavedChanges, setHasUnsavedChanges] = React.useState(false);
const handleBeforeClose = (args: BeforeOpenCloseMenuEventArgs) => {
if (hasUnsavedChanges) {
const confirmed = window.confirm('Unsaved changes. Close anyway?');
if (!confirmed) {
args.cancel = true; // Prevent menu from closing
}
}
};
return (
<ContextMenuComponent
target="#target"
items={menuItems}
beforeClose={handleBeforeClose}
/>
);
}---
onClose
Type: EmitType<OpenCloseMenuEventArgs>
Fires after the menu closes. Use for cleanup or state management.
Example: Clean up after menu closes
function App() {
const handleOnClose = (args: OpenCloseMenuEventArgs) => {
console.log('Menu closed');
// Clear temporary selections or states
document.querySelectorAll('.temp-highlight').forEach(el => {
el.classList.remove('temp-highlight');
});
};
return (
<ContextMenuComponent
target="#target"
items={menuItems}
onClose={handleOnClose}
/>
);
}---
Item Interaction Events
select
Type: EmitType<MenuEventArgs>
Fires when a menu item is clicked/selected. Use to execute actions based on selection.
Event Arguments:
interface MenuEventArgs {
element: HTMLElement; // Menu item element
event: Event; // Click/keyboard event
item?: MenuItemModel; // Selected menu item
items?: MenuItemModel[]; // All menu items
}Example: Execute actions on item selection
function App() {
const handleSelect = (args: MenuEventArgs) => {
const itemText = args.item?.text;
switch (itemText) {
case 'Cut':
document.execCommand('cut');
console.log('Cut executed');
break;
case 'Copy':
document.execCommand('copy');
console.log('Copy executed');
break;
case 'Paste':
document.execCommand('paste');
console.log('Paste executed');
break;
case 'Delete':
if (confirm('Confirm delete?')) {
console.log('Delete executed');
}
break;
}
};
return (
<ContextMenuComponent
target="#target"
items={menuItems}
select={handleSelect}
/>
);
}---
beforeItemRender
Type: EmitType<MenuEventArgs>
Fires before each menu item renders. Use to customize item appearance or add custom logic.
Example: Conditionally disable items
function App() {
const handleBeforeItemRender = (args: MenuEventArgs) => {
const itemText = args.item?.text;
// Disable delete if user doesn't have permission
if (itemText === 'Delete' && !userPermissions.canDelete) {
args.element?.classList.add('e-disabled');
}
// Highlight recent items
if (itemText?.startsWith('Recent:')) {
args.element?.classList.add('recent-item-highlight');
}
// Add keyboard shortcut indicator
const shortcuts: Record<string, string> = {
'Cut': 'Ctrl+X',
'Copy': 'Ctrl+C',
'Paste': 'Ctrl+V'
};
if (shortcuts[itemText as string]) {
const shortcutEl = document.createElement('span');
shortcutEl.className = 'shortcut-hint';
shortcutEl.textContent = shortcuts[itemText as string];
args.element?.appendChild(shortcutEl);
}
};
return (
<ContextMenuComponent
target="#target"
items={menuItems}
beforeItemRender={handleBeforeItemRender}
/>
);
}---
Component Lifecycle
created
Type: EmitType<Event>
Fires after the component is fully created and rendered. Use for initialization.
Example: Initialize after creation
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const handleCreated = () => {
console.log('ContextMenu created and ready');
// Perform initial setup
menuRef.current?.enableItems(['Advanced Options'], userRole === 'admin');
// Add custom data to items
const items = menuRef.current?.items;
if (items) {
items.forEach((item, index) => {
item.id = `item-${index}`;
});
}
};
return (
<ContextMenuComponent
ref={menuRef}
target="#target"
items={menuItems}
created={handleCreated}
/>
);
}---
Menu Items and Data Binding
📄 Read: references/menu-items-and-data-binding.md
- Creating menu items with MenuItemModel
- Using the items property
- Data binding with local data sources
- Dynamic menu item generation from arrays
- Nested submenu configuration
---
Templates and Customization
📄 Read: references/templates-and-customization.md
- Custom item templates (itemTemplate)
- Rendering rich content in menu items
- beforeItemRender event for item customization
- Adding icons and metadata to items
- Conditional rendering
---
Styling and Appearance
📄 Read: references/styling-and-appearance.md
- CSS class customization
- Theme Studio integration
- Custom CSS overrides for menu elements
- Icon styling and positioning
- Visual states (hover, selected, disabled)
---
Accessibility and Keyboard Navigation
📄 Read: references/accessibility-and-keyboard-navigation.md
- WCAG 2.2 and Section 508 compliance
- Screen reader support and ARIA attributes
- Keyboard shortcuts (Esc, Enter, arrow keys)
- Right-to-left (RTL) support
- Focus management
---
Advanced Features
📄 Read: references/advanced-features.md
- Scrollable context menus
- Animation settings and effects
- Menu open types and positioning
- Overflow handling and dynamic layouts
- Configuration for complex scenarios
---
Use Cases and Patterns
📄 Read: references/use-cases-and-patterns.md
- Common context menu patterns
- Adding/removing/enabling/disabling items dynamically
- Rendering separators between items
- Multi-level nesting examples
- Real-world integration scenarios
---
Quick Reference
Essential Code Template
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const menuItems: MenuItemModel[] = [
{ text: 'Cut', iconCss: 'e-icons e-cut', id: 'cut' },
{ text: 'Copy', iconCss: 'e-icons e-copy', id: 'copy' },
{ text: 'Paste', iconCss: 'e-icons e-paste', id: 'paste' },
{ separator: true },
{
text: 'More',
items: [
{ text: 'Delete' },
{ text: 'Properties' }
]
}
];
const handleSelect = (args: any) => {
console.log('Selected:', args.item?.text);
};
const handleBeforeOpen = (args: any) => {
// Customize menu before opening
};
return (
<div>
<div id="target">Right-click me</div>
<ContextMenuComponent
ref={menuRef}
target="#target"
items={menuItems}
select={handleSelect}
beforeOpen={handleBeforeOpen}
animationSettings={{ effect: 'FadeIn', duration: 300 }}
enableScrolling={true}
/>
</div>
);
}
export default App;Common Methods Reference
// Open menu at coordinates
menuRef.current?.open(100, 150);
// Close menu
menuRef.current?.close();
// Enable/disable items
menuRef.current?.enableItems(['Delete', 'Archive'], false);
// Show/hide items
menuRef.current?.showItems(['Delete']);
menuRef.current?.hideItems(['Export']);
// Add items
menuRef.current?.insertAfter(
[{ text: 'New Option' }],
'Existing Item'
);
// Remove items
menuRef.current?.removeItems(['Outdated Item']);
// Get item index
const indices = menuRef.current?.getItemIndex('Copy');---
Next Steps: Choose reference based on your need. Start with getting-started.md, or explore specific features in other references.
Accessibility and Keyboard Navigation
Table of Contents
- WCAG Compliance
- Keyboard Shortcuts
- Screen Reader Support
- ARIA Attributes
- Right-to-Left Support
- Focus Management
- Testing Accessibility
WCAG Compliance
The Syncfusion React ContextMenu component meets accessibility standards:
| Standard | Support | Details |
|---|---|---|
| WCAG 2.2 | ✓ Full | Levels A, AA, AAA compliance |
| Section 508 | ✓ Full | US Federal accessibility requirements |
| ADA | ✓ Full | Americans with Disabilities Act compliance |
| Screen Reader Support | ✓ Full | JAWS, NVDA, VoiceOver compatible |
| Color Contrast | ✓ Full | Meets WCAG AA color contrast ratios |
| Keyboard Navigation | ✓ Full | Complete keyboard accessibility |
| Mobile Device Support | ✓ Full | Touch and mobile interactions |
Keyboard Shortcuts
The ContextMenu supports standard keyboard navigation following WAI-ARIA patterns:
| Key | Action | Purpose |
|---|---|---|
Esc | Close menu | Close the open context menu or submenu |
Enter | Select item | Activate the focused menu item |
Space | Select item | Activate the focused menu item (alternative) |
↑ Arrow Up | Navigate up | Move focus to previous menu item |
↓ Arrow Down | Navigate down | Move focus to next menu item |
← Arrow Left | Close submenu | Close open submenu and focus parent |
→ Arrow Right | Open submenu | Open submenu of focused item |
Example: Keyboard Navigation
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut', iconCss: 'e-icons e-cut' },
{ text: 'Copy', iconCss: 'e-icons e-copy' },
{ text: 'Paste', iconCss: 'e-icons e-paste' },
{ separator: true },
{
text: 'More Options',
items: [
{ text: 'Delete' },
{ text: 'Rename' },
{ text: 'Properties' }
]
}
];
return (
<div>
<div id='target' role='main'>
<h2>Example: Press Ctrl+Shift+X to open context menu</h2>
<p>Right click or use keyboard navigation</p>
</div>
<ContextMenuComponent target='#target' items={menuItems} />
</div>
);
}
export default App;Screen Reader Support
The ContextMenu is fully compatible with assistive technologies:
Tested Screen Readers:
- JAWS (Windows)
- NVDA (Windows, Linux)
- VoiceOver (macOS, iOS)
- TalkBack (Android)
Screen Reader Announcements
When users interact with the context menu: 1. Menu opening is announced: "Context Menu, popup" 2. Menu items are read individually 3. Item state (disabled) is announced 4. Submenu availability is indicated 5. Keyboard shortcuts are announced if provided
Example: Keyboard Shortcut Announcements
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{
text: 'Cut',
iconCss: 'e-icons e-cut',
// Screen readers will announce: "Cut, keyboard shortcut Ctrl+X"
},
{
text: 'Copy',
iconCss: 'e-icons e-copy',
// Screen readers will announce: "Copy, keyboard shortcut Ctrl+C"
},
{
text: 'Paste',
iconCss: 'e-icons e-paste',
// Screen readers will announce: "Paste, keyboard shortcut Ctrl+V"
}
];
return (
<div>
<div id='target'>Right click to open menu</div>
<ContextMenuComponent target='#target' items={menuItems} />
</div>
);
}
export default App;ARIA Attributes
The ContextMenu automatically includes WAI-ARIA attributes for accessibility:
| ARIA Attribute | Value | Purpose |
|---|---|---|
role | menu | Identifies the element as a menu widget |
role | menuitem | Identifies each item as a menu item |
aria-haspopup | true | Indicates menu has a popup submenu |
aria-expanded | true/false | Indicates if submenu is expanded |
aria-label | Item text | Provides accessible label for items |
aria-disabled | true | Indicates item is disabled |
Example: ARIA in Action
// Generated HTML (automatically by Syncfusion)
// <ul role="menu" class="e-contextmenu">
// <li role="menuitem" aria-label="Cut">
// <a href="#">Cut</a>
// </li>
// <li role="menuitem" aria-label="Copy">
// <a href="#">Copy</a>
// </li>
// <li role="menuitem" aria-haspopup="true" aria-expanded="false">
// <a href="#">Edit</a>
// <ul role="menu" aria-hidden="true">...</ul>
// </li>
// </ul>Right-to-Left Support
The ContextMenu supports RTL languages (Arabic, Hebrew, etc.):
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'قص' }, // Arabic: Cut
{ text: 'نسخ' }, // Arabic: Copy
{ text: 'لصق' } // Arabic: Paste
];
return (
<div dir='rtl'>
<div id='target'>انقر بزر الماوس الأيمن لفتح القائمة</div>
<ContextMenuComponent target='#target' items={menuItems} />
</div>
);
}
export default App;Enable RTL:
- Add
dir='rtl'to parent container - Context menu automatically adjusts layout
- Icons and arrows reverse direction
Focus Management
Proper focus management ensures keyboard navigation works correctly:
import { ContextMenuComponent, MenuEventArgs } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const handleBeforeOpen = (args: MenuEventArgs) => {
// Focus management automatically handled by component
// First menu item receives focus when menu opens
// Focus is maintained during navigation
};
const handleBeforeClose = (args: MenuEventArgs) => {
// Focus returns to trigger element when menu closes
};
return (
<div>
<div id='target' tabIndex={0}>
Right click to open menu
</div>
<ContextMenuComponent
ref={menuRef}
target='#target'
beforeOpen={handleBeforeOpen}
beforeClose={handleBeforeClose}
/>
</div>
);
}
export default App;Testing Accessibility
Automated Accessibility Validation
The ContextMenu is tested with:
- Accessibility Checker: Validates WCAG compliance
- Axe Core: Detects accessibility violations
- Jest-Axe: Integration testing with React
Manual Testing Checklist
- [ ] Navigate entire menu using keyboard only
- [ ] All items are reachable via Tab/Arrow keys
- [ ] Screen reader announces all menu items correctly
- [ ] Submenu navigation works with arrow keys
- [ ] Escape key closes menu and returns focus
- [ ] Disabled items are skipped during navigation
- [ ] Menu works in different browsers
- [ ] Touch devices can access context menu
- [ ] High contrast mode displays correctly
Example: Accessibility Testing
// Test keyboard navigation
describe('ContextMenu Accessibility', () => {
it('should navigate menu items with arrow keys', () => {
// Simulate arrow key navigation
const menuItem = document.querySelector('.e-menu-item');
const downArrowEvent = new KeyboardEvent('keydown', {
key: 'ArrowDown',
code: 'ArrowDown'
});
menuItem?.dispatchEvent(downArrowEvent);
// Verify next item is focused
expect(document.activeElement?.textContent).toBe('Copy');
});
it('should close menu with Escape key', () => {
// Simulate Escape key
const escapeEvent = new KeyboardEvent('keydown', {
key: 'Escape',
code: 'Escape'
});
document.dispatchEvent(escapeEvent);
// Verify menu is closed
const contextMenu = document.querySelector('.e-contextmenu-wrapper');
expect(contextMenu?.style.display).toBe('none');
});
});Best Practices
1. Always include text labels: Never rely on icons alone; include descriptive text 2. Use semantic HTML: Leverage the component's built-in ARIA attributes 3. Keyboard-first design: Test all functionality with keyboard navigation 4. Color accessibility: Don't convey meaning through color alone 5. Test with real assistive tech: Use actual screen readers for testing 6. Provide alternatives: Include keyboard shortcuts in documentation 7. Consistent patterns: Follow standard menu interaction patterns 8. Performance: Ensure animations don't interfere with screen readers
Accessibility Resources
- WAI-ARIA Menu Pattern
- WCAG 2.2 Guidelines
- Section 508 Standards
- Syncfusion Accessibility Documentation
Advanced Features
Table of Contents
- Scrollable Context Menus
- Animation Settings
- Menu Open Types
- Overflow Handling
- Position and Offset Configuration
- Menu Effects
- Advanced Scenarios
- Advanced Configuration Properties
- Filter Property
- Hover Delay Property
- Show Item on Click Property
- Enable Persistence Property
- Enable HTML Sanitizer Property
- Locale Property
- CSS Class Property
- Best Practices
Scrollable Context Menus
Enable scrolling for large menu lists that exceed viewport height:
Enable Scrolling
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
// Large menu item list
const menuItems: MenuItemModel[] = Array.from({ length: 30 }, (_, i) => ({
text: `Item ${i + 1}`,
id: `item-${i + 1}`
}));
return (
<div>
<div id='target'>Right click to open scrollable menu</div>
<ContextMenuComponent
target='#target'
items={menuItems}
enableScrolling={true} // Enable scrolling
/>
</div>
);
}
export default App;Customize Scroll Behavior
import { ContextMenuComponent, MenuItemModel, ContextMenuModel, ScrollDirection } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
interface ScrollSettings {
verticalScroll?: boolean;
horizontalScroll?: boolean;
scrollHeight?: string;
scrollWidth?: string;
}
function App() {
const menuItems: MenuItemModel[] = Array.from({ length: 50 }, (_, i) => ({
text: `Menu Item ${i + 1}`,
id: `item-${i + 1}`
}));
// Configure scroll settings
const verticalScrollSettings = {
enable: true,
height: '300px' // Set max height for scrolling
};
const horizontalScrollSettings = {
enable: false
};
return (
<div>
<div id='target'>Right click to open menu</div>
<ContextMenuComponent
target='#target'
items={menuItems}
enableScrolling={true}
/>
</div>
);
}
export default App;CSS for Scrollable Menu
/* Customize scrollbar appearance */
.e-contextmenu-wrapper .e-ul {
max-height: 300px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #999 #f0f0f0;
}
/* Webkit browsers (Chrome, Safari) */
.e-contextmenu-wrapper .e-ul::-webkit-scrollbar {
width: 8px;
}
.e-contextmenu-wrapper .e-ul::-webkit-scrollbar-track {
background: #f0f0f0;
border-radius: 4px;
}
.e-contextmenu-wrapper .e-ul::-webkit-scrollbar-thumb {
background: #999;
border-radius: 4px;
}
.e-contextmenu-wrapper .e-ul::-webkit-scrollbar-thumb:hover {
background: #666;
}Animation Settings
Control menu opening and closing animations:
Configure Animation Effects
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
];
// Define animation settings
const animationSettings = {
effect: 'FadeIn', // FadeIn, SlideDown, Zoom, FadeOut, etc.
duration: 400, // Duration in milliseconds
easing: 'ease-in-out' // CSS easing function
};
return (
<div>
<div id='target'>Right click to open menu with animation</div>
<ContextMenuComponent
target='#target'
items={menuItems}
animationSettings={animationSettings}
/>
</div>
);
}
export default App;Available Animation Effects
| Effect | Description |
|---|---|
None | No animation |
FadeIn | Fade in effect |
FadeOut | Fade out effect |
SlideDown | Slide down from top |
SlideUp | Slide up to top |
Zoom | Zoom in/out |
ZoomIn | Zoom in effect |
ZoomOut | Zoom out effect |
Example: Custom Animation
const animationSettings = {
effect: 'SlideDown',
duration: 300,
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
};
// For submenu opening
const subMenuAnimationSettings = {
effect: 'Zoom',
duration: 200
};Menu Open Types
Control how the context menu opens when triggered:
Open Type Options
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
];
return (
<div>
{/* Right-click to open (default) */}
<div id='target1'>Right click to open</div>
<ContextMenuComponent target='#target1' items={menuItems} />
{/* Context menu on click */}
<div id='target2'>Click to open</div>
<ContextMenuComponent
target='#target2'
items={menuItems}
filter='#target2'
/>
{/* Context menu on hover */}
<div id='target3'>Hover to open</div>
<ContextMenuComponent target='#target3' items={menuItems} />
</div>
);
}
export default App;Overflow Handling
Handle menu overflow when positioned near viewport edges:
Auto-Position on Overflow
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{
text: 'File',
items: [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' },
{ text: 'Close' },
{ text: 'Exit' }
]
},
{
text: 'Edit',
items: [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' },
{ text: 'Select All' }
]
}
];
return (
<div style={{ position: 'relative', height: '500px' }}>
<div id='target' style={{
position: 'absolute',
bottom: '20px',
right: '20px',
width: '100px',
height: '100px',
border: '1px solid gray'
}}>
Right click here
</div>
<ContextMenuComponent
target='#target'
items={menuItems}
// Auto-adjusts position to fit within viewport
/>
</div>
);
}
export default App;Position and Offset Configuration
Control where the context menu appears relative to the click point:
Custom Positioning
import { ContextMenuComponent, MenuEventArgs, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Option 1' },
{ text: 'Option 2' },
{ text: 'Option 3' }
];
const handleBeforeOpen = (args: MenuEventArgs) => {
if (args.event) {
const event = args.event as MouseEvent;
// Get click position
const x = event.clientX;
const y = event.clientY;
// Apply custom offset
const offsetX = 10; // 10px right
const offsetY = 10; // 10px down
console.log(`Menu will open at: ${x + offsetX}, ${y + offsetY}`);
}
};
return (
<div>
<div id='target'>Right click to open menu</div>
<ContextMenuComponent
target='#target'
items={menuItems}
beforeOpen={handleBeforeOpen}
/>
</div>
);
}
export default App;Menu Effects
Apply visual effects to menu elements:
Shadow and Elevation
/* Add shadow elevation */
.e-contextmenu-wrapper {
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
border-radius: 8px;
}
/* Depth effect */
.e-contextmenu-wrapper {
box-shadow:
0 3px 1px -2px rgba(0,0,0,.2),
0 2px 2px 0 rgba(0,0,0,.14),
0 1px 5px 0 rgba(0,0,0,.12);
}Hover Effects
/* Item hover effect */
.e-contextmenu-wrapper .e-menu-item:hover {
background-color: #f5f5f5;
transform: translateX(2px);
transition: all 0.2s ease;
}
/* Scale on hover */
.e-contextmenu-wrapper .e-menu-item:hover {
transform: scale(1.02);
transform-origin: left center;
}Advanced Scenarios
Nested Menus with Custom Structure
function App() {
const menuItems: MenuItemModel[] = [
{
text: 'File',
items: [
{ text: 'New' },
{
text: 'Recent',
items: [
{ text: 'Document 1' },
{ text: 'Document 2' },
{ text: 'Document 3' }
]
},
{ text: 'Open' },
{ separator: true },
{ text: 'Exit' }
]
},
{
text: 'Edit',
items: [
{ text: 'Undo' },
{ text: 'Redo' },
{ separator: true },
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
]
}
];
return (
<div>
<div id='target'>Right click for nested menus</div>
<ContextMenuComponent target='#target' items={menuItems} />
</div>
);
}Dynamic Menu Adjustment
function App() {
const [menuItems, setMenuItems] = React.useState<MenuItemModel[]>([
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
]);
const handleBeforeOpen = (args: MenuEventArgs) => {
// Adjust menu based on context
const target = args.event?.target as HTMLElement;
if (target?.tagName === 'IMG') {
setMenuItems([
{ text: 'Save Image' },
{ text: 'Copy Image' },
{ text: 'View Image' }
]);
} else if (target?.tagName === 'A') {
setMenuItems([
{ text: 'Open Link' },
{ text: 'Copy Link' },
{ text: 'Save Link' }
]);
}
};
return (
<div>
<img id='target' src='image.jpg' alt='Image' />
<a href='#' id='target2'>Link</a>
<ContextMenuComponent target='#target' items={menuItems} beforeOpen={handleBeforeOpen} />
</div>
);
}Advanced Configuration Properties
Filter Property
The filter property restricts context menu activation to specific child elements within the target:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
];
return (
<div>
{/* Context menu appears only on table rows */}
<table id="dataTable">
<tr><td>Row 1</td></tr>
<tr><td>Row 2</td></tr>
<tr><td>Row 3</td></tr>
</table>
<ContextMenuComponent
target="#dataTable"
filter="tr" // Only activate on rows
items={menuItems}
/>
</div>
);
}
export default App;Use Cases:
- Table row operations:
filter="tr" - List item operations:
filter="li" - Dialog-specific actions:
filter=".dialog-content" - Conditional element types:
filter=".editable"
---
Hover Delay Property
The hoverDelay property controls milliseconds before submenu appears on hover:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'File' },
{
text: 'Edit',
items: [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
]
},
{ text: 'View' }
];
return (
<div>
<div id="target">Right-click here</div>
{/* Submenu appears after 600ms hover */}
<ContextMenuComponent
target="#target"
items={menuItems}
hoverDelay={600}
/>
</div>
);
}
export default App;Common Values:
400ms- Default (fast, for experienced users)600ms- Standard (moderate, balanced UX)800ms- Slow (for touchpads or accessibility)
---
Show Item on Click Property
The showItemOnClick property forces submenus to open only on click:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'File' },
{
text: 'Recent',
items: [
{ text: 'Document 1.pdf' },
{ text: 'Document 2.pdf' },
{ text: 'Document 3.pdf' }
]
},
{ text: 'Help' }
];
return (
<div>
<div id="target">Right-click here</div>
{/* Submenus open only on arrow keys or click, not hover */}
<ContextMenuComponent
target="#target"
items={menuItems}
showItemOnClick={true}
/>
</div>
);
}
export default App;Benefits:
- Prevents accidental submenu opening
- Required keyboard navigation (Enter or arrow keys)
- Better for touch interfaces
- Reduces cognitive load
---
Enable Persistence Property
The enablePersistence property saves component state across browser sessions:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{
text: 'File',
items: [
{ text: 'New', expanded: true },
{ text: 'Open' },
{ text: 'Save' }
]
},
{ text: 'Edit' },
{ text: 'View' }
];
return (
<div>
<div id="target">Right-click here</div>
{/* Expanded/collapsed state persists across page reloads */}
<ContextMenuComponent
target="#target"
items={menuItems}
enablePersistence={true}
/>
</div>
);
}
export default App;Storage: Uses browser's localStorage Use Cases:
- Remember user menu preferences
- Maintain menu state across sessions
- Persistent collapse/expand states
---
Enable HTML Sanitizer Property
The enableHtmlSanitizer property sanitizes HTML content to prevent XSS attacks:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
// Potentially unsafe HTML content
const menuItems: MenuItemModel[] = [
{
text: 'Safe Item',
htmlAttributes: {
'title': 'Normal item'
}
},
{
text: 'Item with Script',
// HTML sanitizer will remove scripts
htmlAttributes: {
'data-content': '<img src=x onerror="alert(\'XSS\')">'
}
}
];
return (
<div>
<div id="target">Right-click here</div>
{/* Enable sanitization for user-generated content */}
<ContextMenuComponent
target="#target"
items={menuItems}
enableHtmlSanitizer={true} // Default: true
/>
</div>
);
}
export default App;Security Best Practices:
- Always enable for user-generated content (default: true)
- Disable only for trusted, server-rendered content
- Validate on backend before rendering
- Never trust untrusted URLs or scripts
---
Locale Property
The locale property sets component language and formatting:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cortar', id: 'cut' }, // Spanish: Cut
{ text: 'Copiar', id: 'copy' }, // Spanish: Copy
{ text: 'Pegar', id: 'paste' } // Spanish: Paste
];
return (
<div>
<div id="target">Haz clic derecho aquí</div>
{/* Spanish localization */}
<ContextMenuComponent
target="#target"
items={menuItems}
locale="es-ES"
/>
</div>
);
}
export default App;Common Locale Codes:
en-US- English (default)es-ES- Spanishfr-FR- Frenchde-DE- Germanja-JP- Japanesezh-CN- Chinese (Simplified)ar-AE- Arabiche-IL- Hebrew (RTL)
---
CSS Class Property
The cssClass property adds custom CSS classes to the context menu wrapper:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
];
return (
<div>
<div id="target">Right-click here</div>
<ContextMenuComponent
target="#target"
items={menuItems}
cssClass="custom-dark-menu premium-style"
/>
<style>{`
/* Apply to custom-dark-menu class */
.custom-dark-menu.e-contextmenu {
background: #2d2d2d;
color: #ffffff;
border-color: #555;
}
.custom-dark-menu .e-menu-item {
color: #ffffff;
}
.custom-dark-menu .e-menu-item:hover {
background: #3d3d3d;
}
/* Premium styling */
.premium-style.e-contextmenu {
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0,0,0,0.3);
}
`}</style>
</div>
);
}
export default App;---
Best Practices
1. Animation Performance: Use fast animations (200-300ms) for better UX 2. Scroll Height: Set appropriate max height for scrollable menus (250-350px) 3. Submenu Timing: Use hoverDelay of 500-600ms for balance 4. Filter Usage: Use filter property to limit context menu to specific elements 5. Offset Spacing: Maintain 5-10px offset from click point 6. Viewport Safety: Always ensure menu stays within viewport bounds 7. Touch Targets: Make items at least 44px tall for touch devices 8. Performance: Avoid excessive nesting (3 levels max) 9. Security: Enable HTML sanitizer for user-generated content 10. Accessibility: Use showItemOnClick=true for keyboard-first interfaces
Events and Interaction
Table of Contents
- Context Menu Events
- beforeOpen Event
- beforeClose Event
- onOpen and onClose Events
- select Event
- beforeItemRender Event
- Creating Dialogs on Item Click
- Event Propagation
Context Menu Events
The ContextMenu component provides several events for handling user interactions:
| Event | Trigger | Use Case |
|---|---|---|
beforeOpen | Before menu opens | Prevent opening, customize items |
beforeClose | Before menu closes | Prevent closing, save state |
onOpen | After menu opens | Initialize UI, fetch data |
onClose | After menu closes | Cleanup, save state |
select | Item selected | Execute action, update model |
beforeItemRender | Before item renders | Customize appearance |
beforeOpen Event
The beforeOpen event fires before the context menu opens. Use it to:
- Prevent menu from opening in certain conditions
- Dynamically modify menu items
- Load data before display
Example: Conditional Menu Opening
import { ContextMenuComponent, MenuEventArgs, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const [selectedText, setSelectedText] = React.useState<string>('');
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' },
{ text: 'Delete' }
];
const handleBeforeOpen = (args: MenuEventArgs) => {
// Get selected text
const text = window.getSelection()?.toString() || '';
setSelectedText(text);
// Prevent menu opening if no selection
if (!text) {
args.cancel = true;
console.log('Menu prevented: No text selected');
}
};
return (
<div>
<div id='target' style={{ padding: '20px', border: '1px solid gray' }}>
Select some text and right-click to open the menu
</div>
<ContextMenuComponent
target='#target'
items={menuItems}
beforeOpen={handleBeforeOpen}
/>
</div>
);
}
export default App;Example: Dynamic Item Updates
const handleBeforeOpen = (args: MenuEventArgs) => {
// Update items based on context
const target = args.event?.target as HTMLElement;
const isImageElement = target?.tagName === 'IMG';
// Modify menu items dynamically
if (isImageElement) {
args.items = [
{ text: 'Save Image', iconCss: 'e-icons e-download' },
{ text: 'Copy Image Link', iconCss: 'e-icons e-copy' },
{ text: 'Open in New Tab', iconCss: 'e-icons e-open' }
];
}
};beforeClose Event
The beforeClose event fires before the context menu closes:
import { ContextMenuComponent, MenuEventArgs, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const [hasUnsavedChanges, setHasUnsavedChanges] = React.useState<boolean>(false);
const menuItems: MenuItemModel[] = [
{ text: 'Save' },
{ text: 'Close' }
];
const handleBeforeClose = (args: MenuEventArgs) => {
// Prevent closing if unsaved changes
if (hasUnsavedChanges) {
const confirmed = window.confirm('You have unsaved changes. Close anyway?');
if (!confirmed) {
args.cancel = true;
}
}
};
return (
<div>
<div id='target'>Right click to open menu</div>
<ContextMenuComponent
target='#target'
items={menuItems}
beforeClose={handleBeforeClose}
/>
</div>
);
}
export default App;onOpen and onClose Events
These events fire after the menu has opened or closed:
import { ContextMenuComponent, MenuEventArgs, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const [menuState, setMenuState] = React.useState<'closed' | 'open'>('closed');
const menuItems: MenuItemModel[] = [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' }
];
const handleOnOpen = (args: MenuEventArgs) => {
console.log('Menu opened');
setMenuState('open');
// Perform actions after menu opens
// - Fetch data
// - Initialize components
// - Update UI
};
const handleOnClose = (args: MenuEventArgs) => {
console.log('Menu closed');
setMenuState('closed');
// Perform cleanup after menu closes
// - Save state
// - Cancel pending operations
};
return (
<div>
<div id='target'>Right click to open menu (State: {menuState})</div>
<ContextMenuComponent
target='#target'
items={menuItems}
onOpen={handleOnOpen}
onClose={handleOnClose}
/>
</div>
);
}
export default App;select Event
The select event fires when a menu item is clicked:
Basic Item Selection
import { ContextMenuComponent, MenuEventArgs, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const [selectedAction, setSelectedAction] = React.useState<string>('');
const menuItems: MenuItemModel[] = [
{ text: 'Cut', id: 'cut' },
{ text: 'Copy', id: 'copy' },
{ text: 'Paste', id: 'paste' },
{ text: 'Delete', id: 'delete' }
];
const handleSelect = (args: MenuEventArgs) => {
const itemText = args.item?.text || '';
setSelectedAction(`You selected: ${itemText}`);
console.log('Selected item:', itemText);
console.log('Item ID:', args.item?.id);
// Execute action based on selection
switch (itemText) {
case 'Cut':
document.execCommand('cut');
break;
case 'Copy':
document.execCommand('copy');
break;
case 'Paste':
document.execCommand('paste');
break;
case 'Delete':
console.log('Delete action triggered');
break;
}
};
return (
<div>
<div id='target'>Right click to open menu</div>
<ContextMenuComponent
target='#target'
items={menuItems}
select={handleSelect}
/>
{selectedAction && <p>{selectedAction}</p>}
</div>
);
}
export default App;Advanced Item Selection with Callbacks
const handleSelect = async (args: MenuEventArgs) => {
const itemText = args.item?.text || '';
try {
// Show loading state
console.log(`Processing: ${itemText}...`);
// Execute async operations
switch (itemText) {
case 'Export to PDF':
await exportToPdf();
break;
case 'Send Email':
await sendEmail();
break;
case 'Sync Data':
await syncData();
break;
}
console.log(`${itemText} completed successfully`);
} catch (error) {
console.error(`Error during ${itemText}:`, error);
}
};beforeItemRender Event
The beforeItemRender event allows customization before each item renders:
import { createElement } from '@syncfusion/ej2-base';
import { ContextMenuComponent, MenuEventArgs, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' },
{ text: 'Select All' }
];
const shortcuts: { [key: string]: string } = {
'Cut': 'Ctrl+X',
'Copy': 'Ctrl+C',
'Paste': 'Ctrl+V',
'Select All': 'Ctrl+A'
};
const handleBeforeItemRender = (args: MenuEventArgs) => {
const itemText = args.item?.text || '';
// Add keyboard shortcut span
if (shortcuts[itemText]) {
const shortcutSpan = createElement('span', {
className: 'shortcut-key',
innerHTML: shortcuts[itemText]
});
args.element.appendChild(shortcutSpan);
}
// Disable certain items based on conditions
if (itemText === 'Paste' && !canPaste()) {
args.element.classList.add('e-disabled');
}
};
const canPaste = () => {
// Check if paste is available
return !!navigator.clipboard;
};
return (
<div>
<div id='target'>Right click to open menu</div>
<ContextMenuComponent
target='#target'
items={menuItems}
beforeItemRender={handleBeforeItemRender}
/>
</div>
);
}
export default App;Creating Dialogs on Item Click
Open dialogs or modals when menu items are selected:
import { DialogComponent } from '@syncfusion/ej2-react-popups';
import { ContextMenuComponent, MenuEventArgs, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const [dialogVisible, setDialogVisible] = React.useState<boolean>(false);
const [dialogTitle, setDialogTitle] = React.useState<string>('');
const [selectedItem, setSelectedItem] = React.useState<string>('');
const menuItems: MenuItemModel[] = [
{ text: 'Properties', id: 'properties' },
{ text: 'Settings', id: 'settings' },
{ text: 'About', id: 'about' }
];
const handleSelect = (args: MenuEventArgs) => {
const itemText = args.item?.text || '';
setSelectedItem(itemText);
setDialogTitle(`${itemText} Dialog`);
setDialogVisible(true);
};
const handleDialogClose = () => {
setDialogVisible(false);
};
return (
<div>
<div id='target'>Right click to open context menu</div>
<ContextMenuComponent
target='#target'
items={menuItems}
select={handleSelect}
/>
<DialogComponent
isModal={true}
visible={dialogVisible}
header={dialogTitle}
onClose={handleDialogClose}
>
<p>Content for {selectedItem}</p>
<button onClick={handleDialogClose}>Close</button>
</DialogComponent>
</div>
);
}
export default App;Event Propagation
Control event propagation to prevent unwanted behavior:
const handleSelect = (args: MenuEventArgs) => {
// Stop event propagation to parent elements
if (args.event) {
args.event.stopPropagation();
}
// Prevent default browser behavior
if (args.event) {
args.event.preventDefault();
}
// Execute custom action
console.log('Item selected:', args.item?.text);
};Common Event Patterns
Pattern 1: Multi-Select with Modifiers
const handleSelect = (args: MenuEventArgs) => {
const event = args.event as MouseEvent;
if (event.ctrlKey || event.metaKey) {
// Multi-select mode
console.log('Multi-select:', args.item?.text);
} else if (event.shiftKey) {
// Range select mode
console.log('Range select:', args.item?.text);
} else {
// Single select
console.log('Single select:', args.item?.text);
}
};Pattern 2: Debounced Actions
import { useState, useRef } from 'react';
function App() {
const timeoutRef = useRef<NodeJS.Timeout>();
const handleSelect = (args: MenuEventArgs) => {
// Clear previous timeout
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
// Debounce the action
timeoutRef.current = setTimeout(() => {
console.log('Executing action for:', args.item?.text);
// Execute action
}, 300);
};
return (
<div>
<div id='target'>Right click to open menu</div>
<ContextMenuComponent
target='#target'
items={menuItems}
select={handleSelect}
/>
</div>
);
}
export default App;Best Practices
1. Keep event handlers simple: Move complex logic to separate functions 2. Handle errors gracefully: Wrap async operations in try-catch 3. Cancel propagation when needed: Prevent parent handlers from executing 4. Clean up resources: Remove event listeners when component unmounts 5. Provide user feedback: Show loading states or confirmation dialogs 6. Validate selections: Ensure items can actually be acted upon
Getting Started with Context Menu
Table of Contents
- Setup Development Environment
- Adding Syncfusion Packages
- Adding Style Sheets
- Add ContextMenu Component
- Run the Application
Setup Development Environment
Using Vite (Recommended)
Vite provides a faster development environment, smaller bundle sizes, and optimized builds compared to traditional tools like create-react-app.
Create a new React application:
npm create vite@latest my-app -- --template react
cd my-app
npm installFor TypeScript environment:
npm create vite@latest my-app -- --template react-ts
cd my-app
npm installFor detailed setup instructions, refer to the Vite installation guide.
Using Create React App
Alternatively, you can use create-react-app:
npx create-react-app my-app
cd my-appAdding Syncfusion Packages
Install the Syncfusion ContextMenu component and its dependencies using npm:
npm install @syncfusion/ej2-react-navigations --saveThis command installs:
@syncfusion/ej2-react-navigations(ContextMenu component)- All required peer dependencies listed above
Adding Style Sheets
Add the required CSS stylesheets to your application. Update your src/App.css or create a dedicated styles file:
@import "../node_modules/@syncfusion/ej2-base/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-lists/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css";
/* Context Menu target element styling */
#target {
border: 1px dashed #ccc;
height: 150px;
padding: 10px;
position: relative;
text-align: justify;
color: #666;
user-select: none;
border-radius: 4px;
}Available themes: Replace tailwind3 with your preferred theme:
bootstrap5.3.cssfluent2.cssmaterial3.css
Add ContextMenu Component
Create a basic ContextMenu in your src/App.tsx or src/App.jsx:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
import './App.css';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
];
return (
<div>
<div id="target">Right click / Touch hold to open the ContextMenu</div>
<ContextMenuComponent target="#target" items={menuItems} />
</div>
);
}
export default App;Component Properties
- `target`: CSS selector for the element that triggers the context menu (right-click or touch-hold)
- `items`: Array of menu items using
MenuItemModelinterface - `text`: Display text for each menu item
Run the Application
Start the development server:
npm run devFor create-react-app:
npm startOpen your browser and navigate to http://localhost:5173 (Vite) or http://localhost:3000 (CRA).
Testing the Context Menu
1. Right-click on the target area (or touch-hold on mobile) 2. A popup menu appears with Cut, Copy, Paste options 3. Hover over items to see hover effects 4. Click an item to select it
Complete Getting Started Example
import { enableRipple } from '@syncfusion/ej2-base';
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
import './App.css';
enableRipple(true);
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' },
{ separator: true },
{ text: 'Delete' }
];
return (
<div className="container">
<h2>Context Menu Getting Started</h2>
<div id="target">
Right click / Touch hold to open the ContextMenu
</div>
<ContextMenuComponent
id="contextmenu"
target="#target"
items={menuItems}
/>
</div>
);
}
export default App;Accessing ContextMenu Methods
Use React refs to access component methods for programmatic control:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const menuItems: MenuItemModel[] = [
{ text: 'Cut', id: 'cut' },
{ text: 'Copy', id: 'copy' },
{ text: 'Paste', id: 'paste' }
];
// Programmatically open menu
const openMenu = () => {
menuRef.current?.open(100, 150);
};
// Programmatically close menu
const closeMenu = () => {
menuRef.current?.close();
};
// Enable/disable items
const disablePaste = () => {
menuRef.current?.enableItems(['Paste'], false);
};
// Show/hide items
const hideDelete = () => {
menuRef.current?.hideItems(['Delete']);
};
return (
<div>
<div id="target">Right click to open menu</div>
<div style={{ marginTop: '20px' }}>
<button onClick={openMenu}>Open Menu</button>
<button onClick={closeMenu}>Close Menu</button>
<button onClick={disablePaste}>Disable Paste</button>
<button onClick={hideDelete}>Hide Delete</button>
</div>
<ContextMenuComponent
ref={menuRef}
target="#target"
items={menuItems}
/>
</div>
);
}
export default App;Essential ContextMenu Methods
| Method | Purpose |
|---|---|
open(top, left, target?) | Open menu at coordinates |
close() | Close the menu |
enableItems(items[], enable?, isUniqueId?) | Enable/disable items |
showItems(items[], isUniqueId?) | Show hidden items |
hideItems(items[], isUniqueId?) | Hide items from display |
removeItems(items[], isUniqueId?) | Remove items from menu |
insertAfter(items[], text, isUniqueId?) | Insert items after target |
insertBefore(items[], text, isUniqueId?) | Insert items before target |
getItemIndex(item, isUniqueId?) | Get item index/indices |
setItem(item, id?, isUniqueId?) | Update item properties |
destroy() | Clean up component |
Component Properties Reference
Core Properties
| Property | Type | Default | Description |
|---|---|---|---|
target | string | - | CSS selector for trigger element |
items | MenuItemModel[] | [] | Menu items array |
animationSettings | MenuAnimationSettingsModel | - | Animation configuration |
enableScrolling | boolean | false | Enable scrolling for large menus |
enableRtl | boolean | false | Right-to-left layout |
cssClass | string | - | Custom CSS classes |
filter | string | - | Filter child elements |
hoverDelay | number | 400 | Milliseconds before submenu opens |
showItemOnClick | boolean | false | Force click to open submenus |
enableHtmlSanitizer | boolean | true | Sanitize HTML content |
enablePersistence | boolean | false | Persist state across sessions |
locale | string | en-US | Localization language |
itemTemplate | string \ | Function | - |
Event Properties
| Event | Trigger | Use Case |
|---|---|---|
beforeOpen | Before menu opens | Prevent/modify before opening |
onOpen | After menu opens | Initialize, fetch data |
beforeClose | Before menu closes | Prevent/save state |
onClose | After menu closes | Cleanup |
select | Item clicked | Execute action |
beforeItemRender | Before item renders | Customize appearance |
created | Component initialized | Setup |
Next Steps
- Customize menu items: Add icons, nested menus, and custom templates (see Menu Items and Data Binding)
- Handle events: Respond to item clicks with the
selectevent (see Events and Interaction) - Bind data: Use local data sources to populate menu items dynamically
- Enhance appearance: Apply custom CSS and themes (see Styling and Appearance)
- Ensure accessibility: Implement keyboard navigation and ARIA support (see Accessibility and Keyboard Navigation)
- Explore advanced features: Scrolling, animations, persistence (see Advanced Features)
Menu Items and Data Binding
Table of Contents
- MenuItemModel Interface
- Creating Static Menu Items
- Data Binding with Local Sources
- Dynamic Menu Item Generation
- Nested Submenus
- Item Separators
MenuItemModel Interface
The MenuItemModel interface defines the structure for menu items. Key properties:
| Property | Type | Description |
|---|---|---|
text | string | Display text for the menu item |
id | string | Unique identifier for the item |
iconCss | string | CSS class for the icon |
items | MenuItemModel[] | Nested submenu items |
separator | boolean | Whether to render as a separator |
disabled | boolean | Disable the menu item |
url | string | Navigation URL |
Creating Static Menu Items
Define menu items as a simple array of objects:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut', id: 'cut' },
{ text: 'Copy', id: 'copy' },
{ text: 'Paste', id: 'paste' },
{ text: 'Delete', id: 'delete' }
];
return (
<div>
<div id="target">Right click to open menu</div>
<ContextMenuComponent target="#target" items={menuItems} />
</div>
);
}
export default App;Data Binding with Local Sources
Bind the ContextMenu to local data sources using the items property. The component automatically renders items from the provided data.
Example: Simple Array Binding
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
interface MenuItem {
text: string;
id: string;
}
function App() {
const menuData: MenuItem[] = [
{ text: 'Cut', id: 'cut' },
{ text: 'Copy', id: 'copy' },
{ text: 'Paste', id: 'paste' }
];
const menuItems: MenuItemModel[] = menuData.map(item => ({
text: item.text,
id: item.id
}));
return (
<div>
<div id="target">Right click to open menu</div>
<ContextMenuComponent target="#target" items={menuItems} />
</div>
);
}
export default App;Example: Hierarchical Data Binding
Bind hierarchical data with parent-child relationships:
import { ContextMenuComponent, MenuEventArgs, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
interface IRecord {
id: number;
text: string;
parentId?: number;
}
function App() {
const data: IRecord[] = [
{ id: 1, text: 'File' },
{ id: 2, text: 'New', parentId: 1 },
{ id: 3, text: 'Open', parentId: 1 },
{ id: 4, text: 'Save', parentId: 1 },
{ id: 5, text: 'Edit' },
{ id: 6, text: 'Cut', parentId: 5 },
{ id: 7, text: 'Copy', parentId: 5 },
{ id: 8, text: 'Paste', parentId: 5 }
];
function getMenuItems(): MenuItemModel[] {
const menuItems: MenuItemModel[] = [];
for (const record of data) {
if (record.parentId) {
// Add to parent's items array
const parent = menuItems.find(m => m.id === `item-${record.parentId}`);
if (parent) {
if (!parent.items) parent.items = [];
parent.items.push({ text: record.text, id: `item-${record.id}` });
}
} else {
// Add as root item
menuItems.push({ text: record.text, id: `item-${record.id}` });
}
}
return menuItems;
}
function itemBeforeEvent(args: MenuEventArgs) {
// Optional: Customize items before rendering
if (!args.item.text) {
args.element.classList.add('e-separator');
}
}
return (
<div>
<div id="target">Right click to open menu</div>
<ContextMenuComponent
target="#target"
items={getMenuItems()}
beforeItemRender={itemBeforeEvent}
/>
</div>
);
}
export default App;Dynamic Menu Item Generation
Generate menu items dynamically based on conditions or external data:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
interface FileItem {
name: string;
type: 'file' | 'folder';
}
function App() {
const [selectedFile, setSelectedFile] = React.useState<FileItem | null>(null);
// Generate menu items based on selected file type
function generateMenuItems(): MenuItemModel[] {
const commonItems: MenuItemModel[] = [
{ text: 'Open', iconCss: 'e-icons e-folder-open' },
{ text: 'Cut', iconCss: 'e-icons e-cut' },
{ text: 'Copy', iconCss: 'e-icons e-copy' }
];
if (selectedFile?.type === 'folder') {
return [
...commonItems,
{ text: 'New Folder', iconCss: 'e-icons e-folder' },
{ text: 'Paste', iconCss: 'e-icons e-paste' }
];
}
if (selectedFile?.type === 'file') {
return [
...commonItems,
{ text: 'Rename', iconCss: 'e-icons e-edit' },
{ text: 'Delete', iconCss: 'e-icons e-delete' }
];
}
return commonItems;
}
const handleFileSelect = (file: FileItem) => {
setSelectedFile(file);
};
return (
<div>
<div id="target" onClick={() => handleFileSelect({ name: 'document.txt', type: 'file' })}>
Right click on a file
</div>
<ContextMenuComponent target="#target" items={generateMenuItems()} />
</div>
);
}
export default App;Nested Submenus
Create hierarchical menus with nested items:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{
text: 'File',
items: [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' },
{ separator: true },
{ text: 'Exit' }
]
},
{
text: 'Edit',
items: [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' },
{ separator: true },
{ text: 'Find' }
]
},
{
text: 'View',
items: [
{
text: 'Zoom',
items: [
{ text: '100%' },
{ text: '150%' },
{ text: '200%' }
]
},
{ text: 'Full Screen' }
]
}
];
return (
<div>
<div id="target">Right click to open menu</div>
<ContextMenuComponent target="#target" items={menuItems} />
</div>
);
}
export default App;Item Separators
Add visual separators between menu items using the separator property:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' },
{ separator: true }, // Visual separator
{ text: 'Select All' },
{ separator: true },
{ text: 'Delete' }
];
return (
<div>
<div id="target">Right click to open menu</div>
<ContextMenuComponent target="#target" items={menuItems} />
</div>
);
}
export default App;Best Practices
1. Use unique IDs: Assign unique id values to menu items for easy reference in event handlers 2. Limit nesting depth: Keep submenu nesting to 2-3 levels for better UX 3. Group related items: Use separators to logically group related menu items 4. Label clearly: Use descriptive text that clearly indicates the action 5. Consider state: Disable items that are not applicable to the current context 6. Optimize data: For large datasets, transform data lazily or use virtual scrolling
Styling and Appearance
Table of Contents
- CSS Class Structure
- Basic CSS Customization
- Theme Studio Integration
- Custom CSS Overrides
- Icon Styling
- Visual States
CSS Class Structure
The ContextMenu component uses a structured hierarchy of CSS classes for customization:
| CSS Class | Purpose |
|---|---|
.e-contextmenu-wrapper | Main container for the context menu |
.e-contextmenu-wrapper .e-menu-parent | Parent menu items container |
.e-menu-item | Individual menu item |
.e-menu-item.e-selected | Selected menu item state |
.e-menu-item.e-disabled | Disabled menu item state |
.e-menu-item .e-menu-icon | Icon element in menu item |
.e-menu-item .e-caret | Arrow icon for nested menus |
.e-ul | Unordered list of menu items |
Basic CSS Customization
Override default styles by targeting these classes in your CSS file:
/* Customize the main wrapper */
.e-contextmenu-wrapper {
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
background-color: #fff;
}
/* Customize menu items */
.e-contextmenu-wrapper .e-menu-item {
padding: 10px 16px;
font-size: 14px;
color: #333;
}
/* Hover state */
.e-contextmenu-wrapper .e-menu-item:hover {
background-color: #f0f0f0;
color: #000;
}
/* Selected state */
.e-contextmenu-wrapper .e-menu-item.e-selected {
background-color: #e3f2fd;
color: #1976d2;
font-weight: 500;
}
/* Disabled state */
.e-contextmenu-wrapper .e-menu-item.e-disabled {
color: #ccc;
cursor: not-allowed;
opacity: 0.6;
}
/* Icons */
.e-contextmenu-wrapper .e-menu-item .e-menu-icon {
margin-right: 8px;
font-size: 16px;
}
/* Caret for nested menus */
.e-contextmenu-wrapper .e-menu-item .e-caret::before {
margin-left: 8px;
}Theme Studio Integration
Syncfusion Theme Studio allows visual theme creation without code. To customize themes:
1. Visit Syncfusion Theme Studio 2. Select your base theme (Material, Bootstrap, Fluent, Tailwind) 3. Customize colors, typography, and spacing 4. Download the custom CSS file 5. Import in your application
Example custom theme import:
// In App.css
@import './custom-theme.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css';Custom CSS Overrides
Example: Modern Dark Theme
/* Dark theme styling */
.e-contextmenu-wrapper {
background-color: #2d2d2d;
border: 1px solid #444;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
.e-contextmenu-wrapper .e-menu-item {
color: #e0e0e0;
padding: 10px 14px;
}
.e-contextmenu-wrapper .e-menu-item:hover {
background-color: #3d3d3d;
color: #fff;
}
.e-contextmenu-wrapper .e-menu-item.e-selected {
background-color: #1976d2;
color: #fff;
}
.e-contextmenu-wrapper .e-menu-item.e-disabled {
color: #666;
opacity: 0.5;
}
/* Separator styling */
.e-contextmenu-wrapper .e-separator {
background-color: #444;
margin: 4px 0;
height: 1px;
}Example: Compact Theme
/* Compact spacing */
.e-contextmenu-wrapper {
padding: 2px 0;
}
.e-contextmenu-wrapper .e-menu-item {
padding: 6px 12px;
font-size: 12px;
}
.e-contextmenu-wrapper .e-menu-icon {
font-size: 14px;
margin-right: 6px;
}
.e-contextmenu-wrapper .e-ul {
min-width: 150px;
}Example: Gradient Background
.e-contextmenu-wrapper {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.e-contextmenu-wrapper .e-menu-item {
color: white;
}
.e-contextmenu-wrapper .e-menu-item:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.e-contextmenu-wrapper .e-menu-item.e-selected {
background-color: rgba(255, 255, 255, 0.2);
font-weight: bold;
}Icon Styling
Customize Icon Colors
/* Default icon color */
.e-contextmenu-wrapper .e-menu-icon::before {
color: #666;
}
/* Hover icon color */
.e-contextmenu-wrapper .e-menu-item:hover .e-menu-icon::before {
color: #1976d2;
}
/* Selected icon color */
.e-contextmenu-wrapper .e-menu-item.e-selected .e-menu-icon::before {
color: #fff;
}
/* Custom icon size */
.e-contextmenu-wrapper .e-menu-icon {
font-size: 18px;
}Add Custom Icons
Use Font Awesome or other icon libraries:
<!-- Add Font Awesome CDN -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">const menuItems = [
{ text: 'Edit', iconCss: 'fas fa-edit' },
{ text: 'Delete', iconCss: 'fas fa-trash' },
{ text: 'Download', iconCss: 'fas fa-download' }
];Visual States
Focus State (Keyboard Navigation)
.e-contextmenu-wrapper .e-menu-item:focus {
outline: 2px solid #1976d2;
outline-offset: -2px;
}
.e-contextmenu-wrapper .e-menu-item:focus-visible {
box-shadow: inset 0 0 0 2px #1976d2;
}Submenu Arrow
/* Right-pointing arrow for submenus */
.e-contextmenu-wrapper .e-menu-item .e-caret::before {
content: '\e76e';
margin-left: auto;
color: #999;
}
/* Custom arrow color on hover */
.e-contextmenu-wrapper .e-menu-item:hover .e-caret::before {
color: #1976d2;
}Separator Styles
/* Line separator */
.e-contextmenu-wrapper .e-separator {
height: 1px;
background-color: #e0e0e0;
margin: 4px 0;
}
/* Thick separator */
.e-contextmenu-wrapper .e-separator.thick {
height: 2px;
background-color: #ccc;
margin: 6px 0;
}Advanced Styling Examples
Material Design Style
.e-contextmenu-wrapper {
background: white;
border-radius: 4px;
box-shadow: 0 3px 1px -2px rgba(0,0,0,.2),
0 2px 2px 0 rgba(0,0,0,.14),
0 1px 5px 0 rgba(0,0,0,.12);
min-width: 112px;
}
.e-contextmenu-wrapper .e-menu-item {
padding: 12px 16px;
height: 48px;
display: flex;
align-items: center;
transition: background-color 0.2s ease;
}
.e-contextmenu-wrapper .e-menu-item:hover {
background-color: #f5f5f5;
}
.e-contextmenu-wrapper .e-menu-item.e-selected {
background-color: #f5f5f5;
color: #000;
}Fluent Design Style
.e-contextmenu-wrapper {
background: rgba(255, 255, 255, 0.95);
border: 1px solid #e0e0e0;
border-radius: 2px;
box-shadow: 0 0.63px 2.52px rgba(0, 0, 0, 0.132),
0 1.5px 6px rgba(0, 0, 0, 0.108);
backdrop-filter: blur(1px);
}
.e-contextmenu-wrapper .e-menu-item {
padding: 8px 12px;
height: 32px;
font-size: 13px;
}
.e-contextmenu-wrapper .e-menu-item:hover {
background-color: #f3f3f3;
}Best Practices
1. Consistency: Match context menu styling with your application theme 2. Contrast: Ensure sufficient color contrast for accessibility (WCAG AA minimum) 3. Spacing: Use consistent padding and margins for visual hierarchy 4. Performance: Avoid complex CSS animations that could slow rendering 5. Responsiveness: Test styling on different screen sizes 6. Dark Mode: Provide both light and dark theme variants
Templates and Customization
Table of Contents
- Item Templates
- Template Syntax
- Custom Rendering with beforeItemRender
- Adding Rich Content
- Icon Integration
- Advanced Customization
Item Templates
The itemTemplate property allows you to define custom HTML templates for menu items. This enables rich content beyond simple text, including:
- Custom layouts with multiple content areas
- Icons with descriptions
- Metadata and additional information
- Complex visual hierarchies
Basic Template Example
import { enableRipple } from '@syncfusion/ej2-base';
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
import './App.css';
enableRipple(true);
function App() {
// Define custom template with icon and description
const template: string = `
<div class='menu-wrapper'>
<span class='${iconCss} icon-right'></span>
<div class='text-content'>
<span class='text'>${text}</span>
<span class='description'>${description}</span>
</div>
</div>
`;
const menuItems: any = [
{
text: 'Selection',
description: 'Choose from options',
iconCss: 'e-icons e-list-unordered'
},
{
text: 'Yes / No',
description: 'Select Yes or No',
iconCss: 'e-icons e-check-box'
},
{
text: 'Text Input',
description: 'Type own answer',
iconCss: 'e-icons e-caption',
items: [
{
text: 'Single line',
description: 'Type answer in one line',
iconCss: 'e-icons e-text-form'
},
{
text: 'Multiple line',
description: 'Type answer in multiple lines',
iconCss: 'e-icons e-text-wrap'
}
]
}
];
return (
<div className='control-pane'>
<div id='target'>Right click to open the Context Menu</div>
<ContextMenuComponent
target='#target'
items={menuItems}
itemTemplate={template}
/>
</div>
);
}
export default App;CSS for Template Styling
.menu-wrapper {
display: flex;
align-items: center;
padding: 8px 0;
width: 100%;
}
.icon-right {
font-size: 16px;
margin-right: 10px;
min-width: 24px;
text-align: center;
}
.text-content {
display: flex;
flex-direction: column;
gap: 2px;
}
.text {
font-weight: 500;
font-size: 14px;
color: #333;
}
.description {
font-size: 12px;
color: #999;
font-style: italic;
}
.e-contextmenu-template .e-ul {
min-width: 200px;
}Template Syntax
Template strings use ${propertyName} syntax to access item properties:
// Item object
const item = {
text: 'Edit',
iconCss: 'e-icons e-edit',
shortcut: 'Ctrl+E'
};
// Template
const template = `
<div class='template-item'>
<span class='${iconCss}'></span>
<span class='${text}'></span>
<span class='shortcut'>${shortcut}</span>
</div>
`;Custom Rendering with beforeItemRender
The beforeItemRender event triggers for each item before rendering. Use it to:
- Add custom CSS classes
- Modify item elements dynamically
- Add keyboard shortcuts
- Conditionally apply styling
Example: Add Keyboard Shortcuts
import { createElement, enableRipple } from '@syncfusion/ej2-base';
import { ContextMenuComponent, MenuEventArgs, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
enableRipple(true);
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' },
{ text: 'Select All' }
];
const shortcuts: { [key: string]: string } = {
'Cut': 'Ctrl+X',
'Copy': 'Ctrl+C',
'Paste': 'Ctrl+V',
'Select All': 'Ctrl+A'
};
function beforeItemRender(args: MenuEventArgs) {
if (args.item.text && shortcuts[args.item.text]) {
// Create shortcut span
const shortcutSpan = createElement('span', {
className: 'shortcut',
innerHTML: shortcuts[args.item.text]
});
// Append to item element
args.element.appendChild(shortcutSpan);
}
}
return (
<div>
<div id='target'>Right click to open menu</div>
<ContextMenuComponent
target='#target'
items={menuItems}
beforeItemRender={beforeItemRender}
/>
</div>
);
}
export default App;CSS for Shortcuts
.shortcut {
margin-left: auto;
font-size: 11px;
color: #999;
font-family: monospace;
padding-left: 16px;
min-width: 60px;
text-align: right;
}Adding Rich Content
Combine templates with event handlers for complex customizations:
import { ContextMenuComponent, MenuEventArgs, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const template: string = `
<div class='rich-menu-item'>
<div class='item-icon'>
<img src='${image}' alt='${text}' />
</div>
<div class='item-details'>
<div class='item-title'>${text}</div>
<div class='item-meta'>${category} • ${size}KB</div>
</div>
</div>
`;
const menuItems: any = [
{
text: 'document.pdf',
image: '/icons/pdf.png',
category: 'Document',
size: 245
},
{
text: 'spreadsheet.xlsx',
image: '/icons/excel.png',
category: 'Spreadsheet',
size: 512
},
{
text: 'presentation.pptx',
image: '/icons/powerpoint.png',
category: 'Presentation',
size: 1024
}
];
function beforeOpen(args: MenuEventArgs) {
if (args.element.classList.contains('e-ul')) {
args.element.classList.add('e-rich-menu');
}
}
return (
<div>
<div id='target'>Right click to open menu</div>
<ContextMenuComponent
target='#target'
items={menuItems}
itemTemplate={template}
beforeOpen={beforeOpen}
/>
</div>
);
}
export default App;CSS for Rich Content
.rich-menu-item {
display: flex;
gap: 10px;
padding: 8px;
min-width: 250px;
}
.item-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
border-radius: 4px;
}
.item-icon img {
width: 24px;
height: 24px;
}
.item-details {
flex: 1;
}
.item-title {
font-weight: 500;
font-size: 14px;
color: #333;
}
.item-meta {
font-size: 12px;
color: #999;
margin-top: 2px;
}
.e-rich-menu {
min-width: 270px !important;
}Icon Integration
Add icons to menu items using the Syncfusion icon library:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut', iconCss: 'e-icons e-cut' },
{ text: 'Copy', iconCss: 'e-icons e-copy' },
{ text: 'Paste', iconCss: 'e-icons e-paste' },
{ separator: true },
{ text: 'Delete', iconCss: 'e-icons e-delete' },
{ text: 'Rename', iconCss: 'e-icons e-rename' }
];
return (
<div>
<div id='target'>Right click to open menu</div>
<ContextMenuComponent target='#target' items={menuItems} />
</div>
);
}
export default App;Available Icon Classes:
- Cut:
e-cut - Copy:
e-copy - Paste:
e-paste - Delete:
e-delete - Rename:
e-rename - Refresh:
e-refresh - Download:
e-download - Upload:
e-upload - Settings:
e-settings - Plus:
e-add
Advanced Customization
Conditional Styling
Apply CSS classes based on item state:
function beforeItemRender(args: MenuEventArgs) {
if (args.item.disabled) {
args.element.classList.add('custom-disabled');
}
if (args.item.id === 'special-item') {
args.element.classList.add('custom-highlight');
}
}Dynamic Template Selection
Use different templates based on item properties:
function App() {
const getTemplate = (item: any) => {
if (item.type === 'separator') {
return '<hr class="custom-separator" />';
}
if (item.type === 'header') {
return '<div class="menu-header">${text}</div>';
}
return '<span class="menu-text">${text}</span>';
};
return (
<ContextMenuComponent
target='#target'
items={menuItems}
itemTemplate={getTemplate(menuItems[0])}
/>
);
}Best Practices
1. Performance: Keep templates simple to avoid rendering delays 2. Accessibility: Ensure custom content includes proper ARIA labels 3. Consistency: Match template styling with your application theme 4. Responsiveness: Test templates on different screen sizes 5. Maintenance: Document custom template properties and CSS classes
Use Cases and Patterns
Table of Contents
- Common Context Menu Patterns
- Dynamic Item Management
- Separators and Grouping
- Multi-Level Nesting Examples
- Real-World Integration Scenarios
- File Operations
- Editor Actions
- Data Grid Operations
Common Context Menu Patterns
Pattern 1: Simple Text Operations
Basic context menu for text editing tasks:
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function TextEditorMenu() {
const menuItems: MenuItemModel[] = [
{ text: 'Cut', iconCss: 'e-icons e-cut' },
{ text: 'Copy', iconCss: 'e-icons e-copy' },
{ text: 'Paste', iconCss: 'e-icons e-paste' },
{ separator: true },
{ text: 'Select All', iconCss: 'e-icons e-select-all' }
];
const handleSelect = (args) => {
const text = args.item?.text || '';
switch (text) {
case 'Cut':
document.execCommand('cut');
break;
case 'Copy':
document.execCommand('copy');
break;
case 'Paste':
document.execCommand('paste');
break;
case 'Select All':
document.execCommand('selectAll');
break;
}
};
return (
<div>
<textarea id='target' placeholder='Type something...'></textarea>
<ContextMenuComponent target='#target' items={menuItems} select={handleSelect} />
</div>
);
}
export default TextEditorMenu;Pattern 2: Image Operations
Context menu for image manipulation:
function ImageContextMenu() {
const [selectedImage, setSelectedImage] = React.useState<HTMLImageElement | null>(null);
const menuItems: MenuItemModel[] = [
{ text: 'Save Image', id: 'save', iconCss: 'e-icons e-download' },
{ text: 'Copy Image', id: 'copy', iconCss: 'e-icons e-copy' },
{ separator: true },
{ text: 'View in New Tab', id: 'view', iconCss: 'e-icons e-open' },
{ separator: true },
{ text: 'Image Properties', id: 'properties', iconCss: 'e-icons e-info' }
];
const handleSelect = (args) => {
const image = selectedImage;
if (!image) return;
switch (args.item?.id) {
case 'save':
const link = document.createElement('a');
link.href = image.src;
link.download = 'image.jpg';
link.click();
break;
case 'copy':
navigator.clipboard.write([
new ClipboardItem({ 'image/png': fetch(image.src) })
]);
break;
case 'view':
window.open(image.src, '_blank');
break;
case 'properties':
alert(`Image Size: ${image.naturalWidth}x${image.naturalHeight}`);
break;
}
};
const handleContextMenu = (e: React.MouseEvent<HTMLImageElement>) => {
setSelectedImage(e.currentTarget);
};
return (
<div>
<img
id='target'
src='image.jpg'
alt='Sample'
onContextMenu={handleContextMenu}
/>
<ContextMenuComponent target='#target' items={menuItems} select={handleSelect} />
</div>
);
}
export default ImageContextMenu;Pattern 3: Link Operations
Context menu for hyperlink handling:
function LinkContextMenu() {
const menuItems: MenuItemModel[] = [
{ text: 'Open Link', id: 'open', iconCss: 'e-icons e-open' },
{ text: 'Open in New Tab', id: 'new-tab', iconCss: 'e-icons e-window-new' },
{ separator: true },
{ text: 'Copy Link', id: 'copy', iconCss: 'e-icons e-copy' },
{ text: 'Copy Link Text', id: 'copy-text', iconCss: 'e-icons e-text-copy' },
{ separator: true },
{ text: 'Edit Link', id: 'edit', iconCss: 'e-icons e-edit' },
{ text: 'Remove Link', id: 'remove', iconCss: 'e-icons e-remove' }
];
const handleSelect = (args) => {
const link = document.querySelector('#target') as HTMLAnchorElement;
switch (args.item?.id) {
case 'open':
window.location.href = link.href;
break;
case 'new-tab':
window.open(link.href, '_blank');
break;
case 'copy':
navigator.clipboard.writeText(link.href);
break;
case 'copy-text':
navigator.clipboard.writeText(link.textContent || '');
break;
case 'edit':
console.log('Edit link:', link.href);
break;
case 'remove':
link.replaceWith(link.textContent || '');
break;
}
};
return (
<div>
<a id='target' href='https://example.com'>Example Link</a>
<ContextMenuComponent target='#target' items={menuItems} select={handleSelect} />
</div>
);
}
export default LinkContextMenu;Dynamic Item Management
Add Items Dynamically
function DynamicItemsMenu() {
const [items, setItems] = React.useState<MenuItemModel[]>([
{ text: 'Delete', id: 'delete' }
]);
const addItem = (newItem: MenuItemModel) => {
setItems([...items, newItem]);
};
const removeItem = (id: string) => {
setItems(items.filter(item => item.id !== id));
};
const toggleItem = (id: string) => {
setItems(items.map(item =>
item.id === id ? { ...item, disabled: !item.disabled } : item
));
};
React.useEffect(() => {
// Add items after 2 seconds
setTimeout(() => {
addItem({ text: 'Rename', id: 'rename' });
}, 2000);
// Remove items after 5 seconds
setTimeout(() => {
removeItem('delete');
}, 5000);
}, []);
return (
<div>
<div id='target'>Right click to see dynamic menu</div>
<ContextMenuComponent target='#target' items={items} />
</div>
);
}
export default DynamicItemsMenu;Enable/Disable Items Conditionally
function ConditionalItemsMenu() {
const [hasSelection, setHasSelection] = React.useState(false);
const menuItems: MenuItemModel[] = [
{ text: 'Cut', disabled: !hasSelection },
{ text: 'Copy', disabled: !hasSelection },
{ text: 'Paste', disabled: false },
{ separator: true },
{ text: 'Select All' }
];
const handleBeforeOpen = () => {
const selection = window.getSelection()?.toString();
setHasSelection(!!(selection && selection.length > 0));
};
return (
<div>
<div id='target' onContextMenu={handleBeforeOpen}>
Select text and right-click
</div>
<ContextMenuComponent
target='#target'
items={menuItems}
beforeOpen={handleBeforeOpen}
/>
</div>
);
}
export default ConditionalItemsMenu;Separators and Grouping
Logical Item Grouping
function GroupedMenu() {
const menuItems: MenuItemModel[] = [
// File operations
{ text: 'New', iconCss: 'e-icons e-file-new' },
{ text: 'Open', iconCss: 'e-icons e-folder-open' },
{ text: 'Save', iconCss: 'e-icons e-save' },
{ separator: true },
// Edit operations
{ text: 'Cut', iconCss: 'e-icons e-cut' },
{ text: 'Copy', iconCss: 'e-icons e-copy' },
{ text: 'Paste', iconCss: 'e-icons e-paste' },
{ separator: true },
// Format operations
{ text: 'Bold', iconCss: 'e-icons e-bold' },
{ text: 'Italic', iconCss: 'e-icons e-italic' },
{ text: 'Underline', iconCss: 'e-icons e-underline' },
{ separator: true },
// View/Help
{ text: 'Properties', iconCss: 'e-icons e-properties' }
];
return (
<div>
<div id='target'>Right click for grouped menu</div>
<ContextMenuComponent target='#target' items={menuItems} />
</div>
);
}
export default GroupedMenu;Multi-Level Nesting Examples
File Explorer Pattern
function FileExplorerMenu() {
const menuItems: MenuItemModel[] = [
{ text: 'Create', iconCss: 'e-icons e-new',
items: [
{ text: 'Folder', iconCss: 'e-icons e-folder' },
{ text: 'File', iconCss: 'e-icons e-file' },
{ text: 'Document', iconCss: 'e-icons e-document' }
]
},
{ separator: true },
{ text: 'Cut', iconCss: 'e-icons e-cut' },
{ text: 'Copy', iconCss: 'e-icons e-copy' },
{ text: 'Paste', iconCss: 'e-icons e-paste' },
{ separator: true },
{ text: 'Delete', iconCss: 'e-icons e-delete' },
{ text: 'Rename', iconCss: 'e-icons e-rename' },
{ separator: true },
{ text: 'Properties', iconCss: 'e-icons e-properties' }
];
return (
<div>
<div id='target'>Right click for file menu</div>
<ContextMenuComponent target='#target' items={menuItems} />
</div>
);
}
export default FileExplorerMenu;Application Menu Pattern
function AppMenu() {
const menuItems: MenuItemModel[] = [
{
text: 'File',
items: [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' },
{ separator: true },
{ text: 'Recent Files',
items: [
{ text: 'Document 1' },
{ text: 'Document 2' },
{ text: 'Document 3' }
]
},
{ separator: true },
{ text: 'Exit' }
]
},
{
text: 'Edit',
items: [
{ text: 'Undo' },
{ text: 'Redo' },
{ separator: true },
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
]
},
{
text: 'View',
items: [
{ text: 'Full Screen' },
{ text: 'Zoom',
items: [
{ text: '50%' },
{ text: '100%' },
{ text: '150%' },
{ text: '200%' }
]
}
]
}
];
return (
<div>
<div id='target'>Right click for app menu</div>
<ContextMenuComponent target='#target' items={menuItems} />
</div>
);
}
export default AppMenu;Real-World Integration Scenarios
Integration with Text Editor
function TextEditorWithContext() {
const handleSelect = (args) => {
const editor = document.querySelector('#editor') as HTMLTextAreaElement;
const text = args.item?.text || '';
switch (text) {
case 'Spell Check':
console.log('Running spell check...');
break;
case 'Grammar Check':
console.log('Running grammar check...');
break;
case 'Change Language':
console.log('Opening language selector...');
break;
}
};
const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' },
{ separator: true },
{ text: 'Spell Check', iconCss: 'e-icons e-check' },
{ text: 'Grammar Check', iconCss: 'e-icons e-validation' },
{ separator: true },
{ text: 'Change Language', iconCss: 'e-icons e-globe' }
];
return (
<div>
<textarea id='editor' placeholder='Type here...' />
<ContextMenuComponent target='#editor' items={menuItems} select={handleSelect} />
</div>
);
}
export default TextEditorWithContext;Integration with Task Management
function TaskManagerContext() {
const handleSelect = (args) => {
const item = args.item;
switch (item?.text) {
case 'Mark Complete':
console.log('Marking task as complete');
break;
case 'Assign To':
console.log('Opening assignment dialog');
break;
case 'Set Priority':
console.log('Opening priority selector');
break;
case 'Add Comment':
console.log('Opening comment panel');
break;
case 'Delete Task':
if (confirm('Delete this task?')) {
console.log('Task deleted');
}
break;
}
};
const menuItems: MenuItemModel[] = [
{ text: 'Mark Complete', id: 'complete' },
{ text: 'Assign To', id: 'assign' },
{ separator: true },
{ text: 'Set Priority', id: 'priority',
items: [
{ text: 'Low' },
{ text: 'Medium' },
{ text: 'High' },
{ text: 'Critical' }
]
},
{ separator: true },
{ text: 'Add Comment', id: 'comment' },
{ text: 'Add Attachment', id: 'attach' },
{ separator: true },
{ text: 'Delete Task', id: 'delete' }
];
return (
<div>
<div id='target' className='task-item'>
Task Item
</div>
<ContextMenuComponent target='#target' items={menuItems} select={handleSelect} />
</div>
);
}
export default TaskManagerContext;File Operations
Complete File Management Menu
function FileManagementMenu() {
const handleBeforeOpen = (args) => {
const target = args.event?.target as HTMLElement;
const isReadOnly = target?.classList.contains('read-only');
// Disable edit operations for read-only files
if (isReadOnly) {
args.items = args.items?.map(item => ({
...item,
disabled: ['Rename', 'Delete', 'Move'].includes(item.text as string)
}));
}
};
const menuItems: MenuItemModel[] = [
{ text: 'Open', iconCss: 'e-icons e-open' },
{ text: 'Open With', iconCss: 'e-icons e-open',
items: [
{ text: 'Text Editor' },
{ text: 'Image Viewer' },
{ text: 'Code Editor' }
]
},
{ separator: true },
{ text: 'Cut', iconCss: 'e-icons e-cut' },
{ text: 'Copy', iconCss: 'e-icons e-copy' },
{ text: 'Paste', iconCss: 'e-icons e-paste' },
{ separator: true },
{ text: 'Rename', iconCss: 'e-icons e-rename' },
{ text: 'Delete', iconCss: 'e-icons e-delete' },
{ text: 'Move', iconCss: 'e-icons e-move' },
{ separator: true },
{ text: 'Compress', iconCss: 'e-icons e-compress' },
{ text: 'Properties', iconCss: 'e-icons e-properties' }
];
return (
<div>
<div id='target'>Right click on file</div>
<ContextMenuComponent
target='#target'
items={menuItems}
beforeOpen={handleBeforeOpen}
/>
</div>
);
}
export default FileManagementMenu;Best Practices
1. Logical Grouping: Use separators to group related operations 2. Icon Consistency: Use consistent icons for similar operations 3. Nesting Depth: Limit nesting to 2-3 levels maximum 4. Keyboard Shortcuts: Show available shortcuts in menu items 5. Disabled States: Disable operations that aren't applicable 6. Context Awareness: Adjust menu based on selected element type 7. User Feedback: Provide confirmation for destructive actions 8. Performance: Keep menus responsive with efficient event handlers