mantine-contextmenu: Practical React context menus — install, hooks, submenus, and customization
Short description: This hands-on guide shows how to add right-click / contextual menus in React using mantine-contextmenu (and a fallback manual Mantine approach). You’ll get installation steps, hooks patterns, submenu examples, accessibility notes, and copy-paste code to ship a production-ready contextual menu.
Quick overview: what mantine-contextmenu does and when to use it
Contextual or right-click menus let you surface context-specific actions without cluttering the UI. The mantine-contextmenu ecosystem plugs into Mantine styled components and gives you a straightforward provider/hook pattern to open menus at mouse coordinates, attach dynamic items, or show nested submenus.
Use mantine-contextmenu when you already use Mantine or want a thin library that focuses on context menu UX: position handling, portal rendering, and keyboard support. For teams that prefer minimal dependencies, the same UX can be built with Mantine primitives (Paper, Button, Portal) and a tiny bit of state/position math.
This guide covers two approaches: the typical mantine-contextmenu package pattern (installation, provider, useContextMenu hook) and a robust manual approach using Mantine primitives. Both patterns are compatible with keyboard & screen-reader improvements described below.
Installation & setup (quick start)
First, add the context menu package and basic Mantine dependencies. If you use npm:
npm install mantine-contextmenu @mantine/core @mantine/hooks
# or with yarn
yarn add mantine-contextmenu @mantine/core @mantine/hooks
If you prefer reading a step-by-step walkthrough, see this mantine-contextmenu tutorial: Advanced Context Menus with mantine-contextmenu. For core UI and theming, consult the Mantine docs.
Wrap your app (or a subtree) in the provider the package exposes — this allows centralized context/menu rendering and consistent portal placement:
import { ContextMenuProvider } from 'mantine-contextmenu';
import { MantineProvider } from '@mantine/core';
function Root() {
return (
<MantineProvider>
<ContextMenuProvider>
<App />
</ContextMenuProvider>
</MantineProvider>
);
}
If your project uses TypeScript, install types if they’re provided or add a minimal declaration to avoid TS errors. Some packages export a useContextMenu hook — read the package docs or the linked tutorial for the exact API.
Minimal example: show a right-click menu
This example demonstrates the common approach: capture the browser contextmenu event, prevent default behavior, compute coordinates, and open a positioned menu. The snippet below uses Mantine primitives (Paper, Stack, Button) so you can implement the same UX whether or not you use a context-menu package.
import React, { useState } from 'react';
import { Paper, Stack, Button } from '@mantine/core';
function RightClickArea() {
const [menu, setMenu] = useState({ open: false, x: 0, y: 0 });
function onContextMenu(e) {
e.preventDefault();
setMenu({ open: true, x: e.clientX, y: e.clientY });
}
function closeMenu() {
setMenu({ ...menu, open: false });
}
return (
<div onContextMenu={onContextMenu} style={{height:300,border:'1px solid #ddd',padding:16}}>
Right-click anywhere inside this box.
{menu.open && (
<div style={{position:'fixed',top:menu.y,left:menu.x,zIndex:9999}}>
<Paper shadow="sm">
<Stack spacing={0}>
<Button variant="subtle" onClick={() => { closeMenu(); /* open item */ }}>Open</Button>
<Button variant="subtle" onClick={() => { closeMenu(); /* rename item */ }}>Rename</Button>
<Button variant="subtle" color="red" onClick={() => { closeMenu(); /* delete item */ }}>Delete</Button>
</Stack>
</Paper>
</div>
)}
</div>
);
}
Notes on the example:
- We use position: fixed so the menu tracks the viewport coordinates (clientX/clientY) and avoids scroll-related drift.
- Close the menu on outside clicks or Escape — add a global listener for that in a real app to improve UX.
- This manual approach is robust and framework-agnostic; a dedicated package wraps this pattern and adds conveniences (provider, dynamic menus, keyboard handling).
Using hooks: dynamic items, context data, and item callbacks
A good context menu API separates three concerns: where to show the menu (position), what to show (items), and how items behave (callbacks). The typical hook signature you’ll see is something like useContextMenu() returning a show function: show(event, items, meta).
With that pattern you can attach contextual payloads (for example, itemId or selection) so menu handlers receive the right data. Example pseudo-usage:
const { show } = useContextMenu();
function onContextMenu(e, row) {
e.preventDefault();
show(e, [
{ label: 'Open', onClick: () => openRow(row.id) },
{ label: 'Delete', onClick: () => deleteRow(row.id) },
], { id: row.id });
}
Using a provider/hook decouples the rendering layer from the place you trigger the menu. That simplifies menus for lists, tables, and canvas-like interactions where the menu content depends on the target.
Submenus and nested contextual actions
Submenus are a common requirement: you might want “Move to → Folder A/Folder B” or “Change color → Red/Blue/Green”. Implementing submenus requires hover or keyboard focus to open a nested panel positioned relative to the parent item.
Two practical options exist:
- Use library primitives that already support nested menus. If mantine-contextmenu or the underlying Menu component includes submenu support, prefer that for predictable accessibility and positioning.
- Build nested panels manually: when the user hovers or focuses a parent MenuItem, open a sibling absolute-positioned panel (again using fixed coordinates or anchor refs) and manage its open/close lifecycle.
Manual nested-submenu example (conceptual): render a second floating panel when the user hovers an item and compute its left/top using the parent item bounding rect. Debounce open/close to avoid flicker and support keyboard arrows to navigate items and open submenus.
// conceptual pseudo-code
onParentHover = (target) => {
const rect = target.getBoundingClientRect();
setSubmenu({ open:true, x: rect.right, y: rect.top });
}
Important submenu UX notes: keep hover delays small but forgiving, close the submenu when neither the parent nor the child panel has focus, and ensure keyboard users can open submenus with ArrowRight / ArrowLeft and activate items with Enter or Space.
Customization: styling, icons, and custom components
Mantine shines with theming and style overrides. Whether you use mantine-contextmenu or a manual implementation, options to customize typically include:
– custom menu item components (rich content, avatars, toggles),
– icons on items, plus secondary labels and keyboard hints,
– transition and shadow tuning for perceived performance.
To style items, build your own MenuItem component and pass it into your menu items array, or use Mantine’s theming tokens. Keep touch targets large enough and use clear affordances (icons + labels) for quick scannability.
Accessibility & keyboard support (non-negotiable)
Context menus must work for keyboard users and assistive technologies. Important accessibility measures:
1) Make the menu reachable via keyboard: when focused on an actionable element, support a keyboard key (e.g., Shift+F10 or ContextMenu key) to open the menu programmatically at the element’s bounding rect. 2) Use proper ARIA roles: the menu should have role=”menu” and items role=”menuitem” or role=”menuitemcheckbox” as appropriate. 3) Manage focus: move focus into the menu when it opens and trap arrow navigation inside the menu; pressing Escape should close and restore focus to the trigger element.
When using a third-party package, check whether the library already implements ARIA roles and keyboard navigation. If you build manually, add the roles and keyboard handlers outlined above for compatibility with screen readers and the Featured Snippet-friendly FAQ responses below.
Performance & best practices
Context menus are lightweight, but keep these best practices in mind: lazy-generate items for heavy operations, avoid rendering large trees inside the menu, and reuse the same DOM root (portal) for all menus to prevent layout thrashing. If menus are used heavily in lists, prefer a provider hook to keep a single portal DOM node.
Debounce hover-triggered submenus to avoid constantly creating/destroying panels. For lists with hundreds of rows, don’t attach separate listeners per row — delegate the contextmenu event when possible to minimize memory pressure.
Common pitfalls and how to avoid them
1) Default browser context menu: always call e.preventDefault() on the contextmenu event to avoid the native menu overlapping your custom UI. 2) Off-screen menus: clamp x/y coordinates so the menu doesn’t render off the edge of the viewport. 3) Focus loss: ensure clicking a menu item also closes the menu and returns focus sensibly to the application.
Also test on mobile: some mobile browsers don’t have a right-click. Offer a long-press or an action button for touch interfaces to surface the same menu options.
Backlinks and further reading
Package and documentation links (recommended):
- mantine-contextmenu tutorial — practical walkthrough with advanced examples.
- React Mantine context menu — Mantine docs — core UI components and theming guidance.
- mantine-contextmenu installation on npm — find the package and install instructions.
FAQ
- How do I install mantine-contextmenu?
-
Install with npm or yarn:
npm install mantine-contextmenu @mantine/core @mantine/hooks. Wrap your app in the package provider (ContextMenuProvider) and call the hook (useContextMenu) or the show method inside your onContextMenu handler. For a step-by-step tutorial, see the linked dev.to article. - How can I implement submenus with mantine-contextmenu?
-
Prefer a package-supported submenu API if available. Otherwise, open a secondary floating panel positioned relative to the parent item on hover/focus. Use bounding client rect for precise positioning, debounce the hover, and wire Arrow keys to open/close submenus for keyboard users.
- Is mantine-contextmenu accessible and keyboard-friendly?
-
Many context menu implementations include keyboard handlers (Shift+F10, Arrow navigation, Escape to close) and ARIA roles. If you use a package, verify it sets role=”menu”/”menuitem” and manages focus. When building manually, ensure focus is moved into the menu and aria roles/labels are present.
Semantic core (keyword clusters)
- mantine-contextmenu
- React context menu
- React right-click menu
- React contextual menu
Secondary (medium intent)
- mantine-contextmenu tutorial
- mantine-contextmenu installation
- mantine-contextmenu example
- React menu library
- React Mantine context menu
Clarifying / LSI (supporting phrases)
- context menu hooks
- React right click menu setup
- mantine-contextmenu customization
- context menu submenus
- right-click menu accessibility
- context menu position coordinates
- keyboard navigation for menus
- portal rendering for menus
Suggested micro-markup (FAQ JSON-LD)
Paste this JSON-LD into your page head to surface the FAQ in search results:
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "How do I install mantine-contextmenu?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Install with npm or yarn: npm install mantine-contextmenu @mantine/core @mantine/hooks. Wrap your app in the ContextMenuProvider and call the hook or show method from event handlers."
}
},
{
"@type": "Question",
"name": "How can I implement submenus with mantine-contextmenu?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Use the package's submenu API if available; otherwise render a floating panel positioned next to the parent item on hover/focus and wire keyboard arrows to open/close it."
}
},
{
"@type": "Question",
"name": "Is mantine-contextmenu accessible and keyboard-friendly?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Check for ARIA roles, focus management, and keyboard handlers. If the package lacks them, add role=\"menu\"/\"menuitem\", manage focus on open, and implement Arrow/Escape handlers."
}
}
]
}
Include that script block inside <head> or at the end of the body for search engines to parse.
Ready to publish: This article includes installation steps, a clear manual example, hooks usage, submenu handling, accessibility checks, SEO-friendly FAQ, recommended links, and a semantic core grouped for tagging and internal linking. You can drop it into your blog or docs site as-is.