Contextmenu #
Introduction #
Fuz provides a customizable contextmenu that overrides the system contextmenu to provide helpful capabilities to users. Popular websites with similar features include Google Docs and Discord. Below are caveats about this breaking some user expectations, and a workaround for iOS Safari support. See also the contextmenu_event docs and w3 spec.
When you rightclick inside a ContextmenuRoot, or longpress on touch devices, it
searches the DOM tree for behaviors defined with Contextmenu starting from the target
element up to the root. If any behaviors are found, the Fuz contextmenu opens, showing all contextually
available actions. If no behaviors are found, the default system contextmenu opens.
Here's a ContextmenuRoot with a Contextmenu inside another Contextmenu:
view code
This simple hierarchical pattern gives users the full contextual actions by default -- not just
the actions for the target being clicked, but all ancestor actions too. This means users don't
need to hunt for specific parent elements to find the desired action, unlike many systems --
instead, all actions in the tree are available, improving UX convenience and predictability at
the cost of more noisy menus. Developers can opt out of this inheritance behavior by simply not
nesting Contextmenu declarations, and submenus are useful for managing complexity.
Mouse and keyboard:
- rightclick opens the Fuz contextmenu and not the system contextmenu (minus current exceptions for input/textarea/contenteditable)
- holding Shift opens the system contextmenu, bypassing the Fuz contextmenu
- keyboard navigation and activation should work similarly to the W3C APG menubar pattern
Touch devices:
- longpress opens the Fuz contextmenu and not the system contextmenu (minus current exceptions for input/textarea/contenteditable)
- tap-then-longpress opens the system contextmenu or performs other default behavior like selecting text, bypassing the Fuz contextmenu
- tap-then-longpress can't work for clickable elements like links; longpress on the first contextmenu entry for those cases (double-longpress)
All devices:
- opening the contextmenu on the contextmenu itself shows the system contextmenu, bypassing the Fuz contextmenu
- opening the contextmenu attempts haptic feedback with
navigator.vibrate
Disable default behaviors #
Check the boxes below to disable automatic a link detection and copy text detection, and see how the contextmenu behaves.
<ContextmenuRoot> When no behaviors are defined, the system contextmenu is shown instead.
Expected: the Fuz contextmenu will open and show:
- custom "some custom entry" entry
- "copy text" entry when text is selected
- link entry when clicking on a link
iOS compatibility #
Fuz provides two versions of the contextmenu root component with different tradeoffs due to iOS
Safari not supporting the contextmenu event as of October 2025, see WebKit bug #213953.
Use ContextmenuRoot by default for better performance and haptic feedback. Use ContextmenuRootForSafariCompatibility only if you need iOS Safari support.
ContextmenuRoot
- standard, default implementation
- relies on the browser's contextmenu event
- much simpler, better performance with fewer and less intrusive event handlers, fewer edge cases that can go wrong
- does not work on iOS Safari until WebKit bug #213953 is fixed
ContextmenuRootForSafariCompatibility
- opt-in for iOS
- some browsers (including mobile Chrome) block
navigator.vibratehaptic feedback due to the timeout-based gesture detection (because it's not a direct user action) - implements custom longpress detection to work around iOS Safari's lack of
contextmenuevent support - works on all devices including iOS Safari
- more complex implementation with custom touch event handling and gesture detection
- a longpress is cancelled if you move the touch past a threshold before it triggers
- opt into this version only if you need iOS Safari support
Caveats #
The Fuz contextmenu provides powerful app-specific UX, but it breaks from normal browser behavior by replacing the system contextmenu.
To mitigate the downsides:
- The Fuz contextmenu only replaces the system contextmenu when the DOM tree has defined
behaviors. Note that
alinks have default contextmenu behaviors unless disabled. Other interactive elements may have default behaviors added in the future. - The Fuz contextmenu does not open on elements that allow clipboard pasting like inputs, textareas, and contenteditables -- however this may change for better app integration, or be configurable.
- To bypass on devices with a keyboard, hold Shift while rightclicking.
- To bypass on touch devices (e.g. to select text), use tap-then-longpress instead of longpress.
- Triggering the contextmenu inside the Fuz contextmenu shows the system contextmenu.
See also the contextmenu_event docs and the w3 spec.