Context Menu
Displays a menu to the user triggered by a right-click with a list of actions or functions.
Installation
Add the component to your project:
rails generate shadcn:component context_menu
Usage
<%= render Shadcn::ContextMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<div class="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm">
Right click here
</div>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_label { "Actions" } %>
<% content.with_separator %>
<% content.with_item(href: "#") { "Back" } %>
<% content.with_item(href: "#", disabled: true) { "Forward" } %>
<% content.with_item(href: "#") { "Reload" } %>
<% content.with_separator %>
<% content.with_item(href: "#") { "Save As..." } %>
<% content.with_item(href: "#") { "Print" } %>
<% end %>
<% end %>
Examples
Basic
<%= render Shadcn::ContextMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<div class="flex h-[100px] w-[200px] items-center justify-center rounded-md border border-dashed text-sm">
Right click
</div>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") { "Edit" } %>
<% content.with_item(href: "#") { "Duplicate" } %>
<% content.with_item(href: "#") { "Archive" } %>
<% end %>
<% end %>
With Icons
Add icons to menu items for better visual clarity.
<%= render Shadcn::ContextMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<div class="flex h-[100px] w-[200px] items-center justify-center rounded-md border border-dashed text-sm">
Right click
</div>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") do %>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
Edit
<% end %>
<% content.with_item(href: "#") do %>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
Copy
<% end %>
<% content.with_item(href: "#") do %>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>
Delete
<% end %>
<% end %>
<% end %>
With Separators and Labels
Use separators and labels to organize menu items into logical groups.
<%= render Shadcn::ContextMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<div class="flex h-[100px] w-[200px] items-center justify-center rounded-md border border-dashed text-sm">
Right click
</div>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_label { "Edit" } %>
<% content.with_separator %>
<% content.with_item(href: "#") { "Cut" } %>
<% content.with_item(href: "#") { "Copy" } %>
<% content.with_item(href: "#") { "Paste" } %>
<% content.with_separator %>
<% content.with_label { "View" } %>
<% content.with_separator %>
<% content.with_item(href: "#") { "Zoom In" } %>
<% content.with_item(href: "#") { "Zoom Out" } %>
<% end %>
<% end %>
With Keyboard Shortcuts
Display keyboard shortcuts to help users discover shortcuts.
<%= render Shadcn::ContextMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<div class="flex h-[100px] w-[200px] items-center justify-center rounded-md border border-dashed text-sm">
Right click
</div>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") do |item| %>
Cut
<% item.with_shortcut { "⌘X" } %>
<% end %>
<% content.with_item(href: "#") do |item| %>
Copy
<% item.with_shortcut { "⌘C" } %>
<% end %>
<% content.with_item(href: "#") do |item| %>
Paste
<% item.with_shortcut { "⌘V" } %>
<% end %>
<% content.with_separator %>
<% content.with_item(href: "#") do |item| %>
Select All
<% item.with_shortcut { "⌘A" } %>
<% end %>
<% end %>
<% end %>
Destructive Items
Use the destructive variant for dangerous actions.
<%= render Shadcn::ContextMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<div class="flex h-[100px] w-[200px] items-center justify-center rounded-md border border-dashed text-sm">
Right click
</div>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") { "View Details" } %>
<% content.with_item(href: "#") { "Edit" } %>
<% content.with_separator %>
<% content.with_item(variant: :destructive, href: "#") { "Delete" } %>
<% end %>
<% end %>
Disabled Items
Disable menu items when an action is unavailable.
<%= render Shadcn::ContextMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<div class="flex h-[100px] w-[200px] items-center justify-center rounded-md border border-dashed text-sm">
Right click
</div>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") { "Copy" } %>
<% content.with_item(href: "#", disabled: true) { "Paste (clipboard empty)" } %>
<% content.with_item(href: "#") { "Select All" } %>
<% end %>
<% end %>
With Checkbox Items
Use checkbox items for toggleable options within the context menu.
<%= render Shadcn::ContextMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<div class="flex h-[100px] w-[200px] items-center justify-center rounded-md border border-dashed text-sm">
Right click
</div>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_label(inset: true) { "Appearance" } %>
<% content.with_separator %>
<% content.with_checkbox_item(checked: true) { "Show Toolbar" } %>
<% content.with_checkbox_item(checked: false) { "Show Sidebar" } %>
<% content.with_checkbox_item(checked: true) { "Show Status Bar" } %>
<% content.with_separator %>
<% content.with_checkbox_item(disabled: true) { "Enable Notifications" } %>
<% end %>
<% end %>
With Radio Items
Use radio groups for mutually exclusive options.
<%= render Shadcn::ContextMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<div class="flex h-[100px] w-[200px] items-center justify-center rounded-md border border-dashed text-sm">
Right click
</div>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_label(inset: true) { "Sort By" } %>
<% content.with_separator %>
<% content.with_radio_group(value: "date") do |group| %>
<% group.with_item(value: "name") { "Name" } %>
<% group.with_item(value: "date", checked: true) { "Date Modified" } %>
<% group.with_item(value: "size") { "Size" } %>
<% group.with_item(value: "type") { "Type" } %>
<% end %>
<% content.with_separator %>
<% content.with_label(inset: true) { "Order" } %>
<% content.with_separator %>
<% content.with_radio_group(value: "asc") do |group| %>
<% group.with_item(value: "asc", checked: true) { "Ascending" } %>
<% group.with_item(value: "desc") { "Descending" } %>
<% end %>
<% end %>
<% end %>
API Reference
ContextMenuComponent
The main context menu wrapper component. Triggered by right-clicking on the trigger area.
Slots
| Slot | Description |
|---|---|
| with_trigger | The area that responds to right-clicks |
| with_menu | The context menu content container (yields ContextMenuContentComponent) |
ContextMenuContentComponent Slots
| Slot | Props | Description |
|---|---|---|
| with_item | href:, variant:, disabled:, inset: |
Menu item (can be a link or div) |
| with_checkbox_item | checked:, disabled: |
Toggleable checkbox menu item |
| with_radio_group | value: |
Group of mutually exclusive radio items |
| with_label | inset: |
Section label for grouping items |
| with_separator | - | Visual separator between menu sections |
ContextMenuItemComponent
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
| href |
String
|
nil
|
Link URL (renders as <a> if provided, <div> otherwise) |
| variant |
Symbol
|
:default
|
Item variant (:default, :destructive) |
| disabled |
Boolean
|
false
|
Whether the item is disabled |
| inset |
Boolean
|
false
|
Whether to add left padding for icon alignment |
ContextMenuItemComponent Slots
| Slot | Description |
|---|---|
| with_shortcut | Keyboard shortcut hint displayed on the right |
Stimulus Controller
This component requires JavaScript. The Stimulus controller shadcn--context-menu provides interactivity.
Manages context menu open/close state, positioning at mouse cursor, keyboard navigation, and item selection.
Installation
Add to your config/importmap.rb:
pin "shadcn-rails", to: "shadcn/index.js"
Then in your app/javascript/controllers/index.js:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails"
const application = Application.start()
registerShadcnControllers(application)
Install the package:
npm install shadcn-rails
Then in your app/javascript/controllers/index.js:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails"
const application = Application.start()
registerShadcnControllers(application)
Install the package:
yarn add shadcn-rails
Then in your app/javascript/controllers/index.js:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails"
const application = Application.start()
registerShadcnControllers(application)
Install the package:
npm install shadcn-rails
Then in your app/javascript/controllers/index.js:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails"
const application = Application.start()
registerShadcnControllers(application)
Or import just this controller:
import ContextMenuController from "shadcn-rails/controllers/context_menu_controller"
application.register("shadcn--context-menu", ContextMenuController)
Targets
| Name | Description |
|---|---|
| trigger | The area that responds to right-click events |
| content | The context menu content container |
| item | Individual menu items for keyboard navigation |
Values
| Name | Type | Default | Description |
|---|---|---|---|
| open | Boolean |
false |
Current open/closed state |
Actions
| Action | Description |
|---|---|
| show | Opens the context menu at mouse position |
| hide | Closes the context menu |
| close | Alias for hide |
| selectItem | Handles item selection and closes menu |
TypeScript
Type definitions are included. Import types as needed:
import type { ContextMenuController } from "shadcn-rails"
Implementing Keyboard Shortcuts
Note: The keyboard shortcuts displayed in context menu items (via with_shortcut) are visual hints only.
They do not automatically execute when the keys are pressed. This matches the behavior of shadcn/ui's Context Menu.
To implement actual keyboard shortcuts, you'll need to add a global keydown listener. Here's an example using a Stimulus controller:
// app/javascript/controllers/keyboard_shortcuts_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
this.boundHandleKeydown = this.handleKeydown.bind(this)
document.addEventListener("keydown", this.boundHandleKeydown)
}
disconnect() {
document.removeEventListener("keydown", this.boundHandleKeydown)
}
handleKeydown(event) {
// Check for Cmd (Mac) or Ctrl (Windows/Linux)
const modifier = event.metaKey || event.ctrlKey
if (modifier && event.key === "z") {
event.preventDefault()
this.undo()
} else if (modifier && event.key === "c") {
event.preventDefault()
this.copy()
} else if (modifier && event.key === "v") {
event.preventDefault()
this.paste()
}
}
undo() { /* implement undo logic */ }
copy() { /* implement copy logic */ }
paste() { /* implement paste logic */ }
}
Then apply the controller to your page or application layout:
<body data-controller="keyboard-shortcuts">
<!-- Your page content -->
</body>
Accessibility
- Uses
role="menu"for the context menu content - Sets
role="menuitem"on each item - Supports full keyboard navigation:
- Escape - Closes the menu
- ↓ / ↑ - Navigate through menu items
- Home / End - Jump to first/last item
- Enter / Space - Select focused item
- Disabled items use
data-disabledattribute and are skipped in keyboard navigation - Automatically closes when clicking outside the context menu
- Menu is positioned at mouse cursor location, adjusted to stay within viewport
- Focus is automatically moved to the first menu item when opened
On This Page