webdevpuneet.com

Context Menu — Free HTML CSS JS Right-Click Snippet

Context Menu · Navigation · Plain HTML, CSS & JS · Live preview

CategoryNavigation

Copy & paste

Source Code

<div class="stage" id="stage" oncontextmenu="showMenu(event)">
  <div class="instructions">
    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 11V6a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v0"/><path d="M14 10V4a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v0"/><path d="M10 10.5V6a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v0"/><path d="M18 11a2 2 0 0 1 4 0v3a8 8 0 0 1-16 0V5"/></svg>
    Right-click anywhere in this area
  </div>
</div>

<ul class="ctx-menu" id="ctx">
  <li onclick="action('Copy')"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>Copy</li>
  <li onclick="action('Cut')"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="6" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><line x1="20" y1="4" x2="8.12" y2="15.88"/><line x1="14.47" y1="14.48" x2="20" y2="20"/><line x1="8.12" y1="8.12" x2="12" y2="12"/></svg>Cut</li>
  <li onclick="action('Paste')"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><rect x="8" y="2" width="8" height="4" rx="1"/></svg>Paste</li>
  <li class="divider"></li>
  <li onclick="action('Rename')"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>Rename</li>
  <li onclick="action('Share')"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>Share</li>
  <li class="divider"></li>
  <li class="danger" onclick="action('Delete')"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6M14 11v6"/></svg>Delete</li>
</ul>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

contextmenu event with e.preventDefault() blocks browser default menu
Position clamped: Math.min(clientX, innerWidth-180) prevents overflow
scale(0.95)+opacity: 0 to scale(1)+opacity:1 pop-in animation over 0.12s
Document click outside closes menu via closest(".ctx-menu") check
ESC keydown closes menu via keydown listener
Each item onclick calls hideMenu() then performs action
min-width: 170px menu with icon + label flex rows
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Context Menu — contextmenu Event, Viewport Clamping, Pop-in Animation & Three Close Methods

Screenshot of the Context Menu snippet rendered live

A context menu is the floating menu that appears when a user right-clicks (or long-presses on touch). It surfaces contextual actions relevant to the specific element that was right-clicked — Copy, Paste, Rename, Delete — without consuming any permanent screen space, like a click-triggered dropdown menu. Context menus are a staple of desktop-class web interfaces: file managers, data tables, spreadsheet editors, image editors, map applications, and any interface that aims to match native application interactions.

The contextmenu event and preventDefault

The document.addEventListener('contextmenu', showMenu) listener intercepts every right-click on the stage area. e.preventDefault() is the critical line — without it, the browser's built-in context menu appears on top of the custom one. After prevention, e.clientX and e.clientY give the cursor position relative to the viewport, which becomes the menu's top-left position.

Viewport edge clamping

A context menu positioned directly at clientX, clientY will overflow the right or bottom edge when the user right-clicks near a viewport boundary. The clamping calculation prevents this: const x = Math.min(e.clientX, window.innerWidth - 180) — 180 is the menu's minimum width. If the cursor is within 180px of the right edge, the menu is shifted left to fit. Similarly for Y: Math.min(e.clientY, window.innerHeight - 240) — 240 is the approximate menu height. This calculation is a common source of bugs in context menu implementations that skip it and only discover the issue during testing near edges.

The pop-in animation from the click point

The menu has default CSS transform: scale(0.95); opacity: 0; pointer-events: none; transition: opacity 0.12s, transform 0.12s. Adding .show transitions to transform: scale(1); opacity: 1; pointer-events: all. The transform-origin: top left anchors the scale animation to the menu's top-left corner — which is closest to the cursor position — making the menu appear to grow outward from the click point. This feels more natural than a centre-origin scale or a slide animation.

Three dismissal mechanisms

Well-implemented context menus have three ways to close: clicking a menu item (which performs an action then dismisses), clicking anywhere outside the menu (the standard dismiss gesture), and pressing the Escape key (keyboard-accessible dismiss). The document.addEventListener('click', ...) listener removes .show on any click. The document.addEventListener('keydown', e => { if (e.key === 'Escape') ... }) handles keyboard dismiss. Each menu item's onclick handler calls menu.classList.remove('show') after performing its action.

Danger item styling

The Delete item has a .danger class that applies color: #dc2626 (red) and background: #fef2f2 on hover, following the universal convention that destructive actions are shown in red to warn users before they click.

Step by step

How to Use

  1. 1
    Right-click anywhere in the dashed stage areaRight-click in the stage area to open the context menu at the cursor position. The menu pops in with a scale + opacity animation from its top-left corner. Click a menu item, click outside, or press Escape to close it. Try right-clicking near the edge of the preview to see the viewport clamping in action.
  2. 2
    Click a menu item to see the action and dismissClick any menu item (Copy, Cut, Paste, Rename, Share, Delete) to trigger an alert with the item name and automatically dismiss the menu. In your implementation, replace the alert() call inside the action() function with your actual handler logic for each item.
  3. 3
    Add, remove, or reorder menu items in the HTMLIn the HTML panel, each <li> inside .ctx-menu is a menu item. Add new items by copying an existing li and updating the SVG icon and text. The .divider class on a li creates a horizontal separator line. The .danger class on the Delete item applies the red destructive-action styling.
  4. 4
    Bind to a specific element instead of the whole documentIn the JS panel, change the contextmenu listener target from the stage element's oncontextmenu attribute to a specific element: myElement.addEventListener('contextmenu', showMenu). This shows the menu only when right-clicking that specific element. Use event delegation with e.target.closest() for dynamic elements in lists or tables.
  5. 5
    Add a submenu to a menu itemAdd a nested .ctx-menu ul as a child of a li item. In CSS, set the nested menu to display: none by default and display: block on .has-submenu:hover .ctx-menu. Position it with left: 100%; top: 0 to open to the right of the parent item. Add the viewport clamping logic to detect when the submenu would overflow the right edge and flip it to left: -100%.
  6. 6
    Export as HTML, JSX, or Tailwind for your projectClick HTML for a standalone file ready to use in any web project, JSX for a React ContextMenu component with onContextMenu, x, y, and items props, or Tailwind for a Tailwind CSS version. The JSX export manages visibility via useState and attaches close listeners via useEffect with cleanup on unmount.

Real-world uses

Common Use Cases

File manager and document editor right-click actions
Right-clicking a file in a web-based file manager reveals Cut, Copy, Paste, Rename, Move to Trash actions. Right-clicking a text selection in an editor reveals Format, Bold, Link, Comment options. The context menu pattern is the standard interaction model for any web application that aims to provide a desktop-class experience rather than mobile-style bottom sheets.
Data table and spreadsheet grid row actions
Right-clicking a table row reveals Edit Row, Duplicate, Export as CSV, Archive, Delete actions without consuming any column space or adding visible action buttons to every row. The menu appears exactly at the cursor position and closes when an action is performed or the user clicks elsewhere, keeping the table UI clean and scannable at all times.
Learn contextmenu event handling and viewport clamping math
The contextmenu event fires on right-click. e.preventDefault() suppresses the browser's native menu. The position calculation Math.min(e.clientX, window.innerWidth - menuWidth) prevents right-edge overflow — a bug that is invisible during development (where you rarely right-click in the corner) but discovered immediately by users. Study this clamping pattern as it applies equally to tooltips, dropdowns, and any absolutely-positioned floating element.
Map, canvas, and diagram right-click interactions
Right-clicking on a map tile shows Add Marker, Get Directions, Copy Coordinates. Right-clicking a canvas node in a diagram editor shows Edit, Connect, Delete, Group. Right-clicking an image on a canvas shows Save, Crop, Flip, Rotate. The context menu position is calculated from e.clientX and e.clientY regardless of the element type, making it universally applicable to any right-click target.
Browser extension content script menu injection
A browser extension content script can inject a custom context menu by listening to the contextmenu event on the page and rendering a custom menu div. Use cases include text translation (right-click selected text), link management (right-click a link to save or bookmark), and image processing (right-click an image to download, reverse-search, or extract text). The same positioning and clamping logic works in injected scripts.
Node-based editor and whiteboard canvas menus
No-code tools, workflow builders, and whiteboard applications use right-click context menus extensively. Right-clicking a node shows Add Connected Node, Edit Properties, Duplicate, Lock, Delete. Right-clicking the canvas background shows Add Node, Paste, Select All, Fit View. Each target element type shows a different menu by detecting the right-click target via e.target.closest().
Related: Dot Pagination Carousel
See the Dot Pagination Carousel for a related navigation pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

The showMenu function calculates the clamped position before setting the menu's left and top: const x = Math.min(e.clientX, window.innerWidth - 180) prevents the menu from overflowing the right edge by capping the left position so the full 180px-wide menu fits. const y = Math.min(e.clientY, window.innerHeight - 240) prevents bottom overflow for the approximate 240px menu height. Both values use Math.min — if the cursor position leaves enough room, the menu appears at the cursor; if not, it is shifted inward just enough to fit. This ensures the menu is always fully visible regardless of where in the viewport the user right-clicks.

The .ctx-menu element has transform: scale(0.95); opacity: 0 in its default state, combined with transition: opacity 0.12s, transform 0.12s and pointer-events: none. Adding the .show class changes these to transform: scale(1); opacity: 1; pointer-events: all, triggering the CSS transition. The transform-origin: top left is critical — it anchors the scale animation to the top-left corner of the menu, which is the corner closest to the cursor (since the menu opens to the right and below the click point). Without this, the menu would scale from its geometric center, creating a floating animation that doesn't feel attached to the click position.

Use event delegation with e.target.closest() to detect which element type was right-clicked. In the showMenu function: const fileEl = e.target.closest('[data-type="file"]'); const rowEl = e.target.closest('[data-type="row"]'); Then render different menu items based on which element matched. An elegant pattern is to store menu item definitions per type in an object: const menuItems = { file: [{label:'Copy',...},{label:'Delete',...}], row: [{label:'Edit',...},...] }. Pass the matched type to a renderMenu() function that populates the ul from the correct items array.

The contextmenu event does not fire consistently on touch devices. For mobile context menus, add a touchstart listener that starts a 500ms timer: const timer = setTimeout(() => showMenu(touch.clientX, touch.clientY), 500). In touchend and touchmove listeners, call clearTimeout(timer) to cancel if the user lifts their finger or moves before 500ms. On iOS, also call e.preventDefault() in touchstart to prevent the browser's default callout menu from appearing. This pattern reliably detects long-press and is the standard technique for touch-based contextual menus.

Yes. While the Context Menu snippet gives you one pattern to copy, the free Responsive Navbar Builder at webdevpuneet.com/navbar-builder/ designs a whole navigation bar: logo, links and dropdown menus, a mobile hamburger menu (dropdown panel, slide-in drawer or full screen) and a scroll effect (sticky, shrink, hide on scroll or transparent). It has six presets, a live preview that runs the real code, and exports HTML + CSS + vanilla JS, React, Tailwind, Vue 3 or Angular — or you can click Fork & Edit to keep editing it in My Code.