A contextual menu that appears on right-click or long-press, offering quick actions relevant to the selected element.
@radix-ui/react-context-menu
lucide-react1import {
2 ContextMenu,
3 ContextMenuCheckboxItem,
4 ContextMenuContent,
5 ContextMenuItem,
6 ContextMenuLabel,
7 ContextMenuRadioGroup,
8 ContextMenuRadioItem,
9 ContextMenuSeparator,
10 ContextMenuShortcut,
11 ContextMenuSub,
12 ContextMenuSubContent,
13 ContextMenuSubTrigger,
14 ContextMenuTrigger,
15} from '@/components/ui/context-menu'
16
17export function ContextMenuDemo() {
18 return (
19 <ContextMenu>
20 <ContextMenuTrigger className="border-gray flex h-[50px] w-[250px] items-center justify-center rounded-md border text-sm">
21 Right click here
22 </ContextMenuTrigger>
23 <ContextMenuContent className="w-52">
24 <ContextMenuItem inset>
25 Back
26 <ContextMenuShortcut>⌘[</ContextMenuShortcut>
27 </ContextMenuItem>
28 <ContextMenuItem inset disabled>
29 Forward
30 <ContextMenuShortcut>⌘]</ContextMenuShortcut>
31 </ContextMenuItem>
32 <ContextMenuItem inset>
33 Reload
34 <ContextMenuShortcut>⌘R</ContextMenuShortcut>
35 </ContextMenuItem>
36 <ContextMenuSub>
37 <ContextMenuSubTrigger inset>
38 More Tools
39 </ContextMenuSubTrigger>
40 <ContextMenuSubContent className="w-44">
41 <ContextMenuItem>Save Page...</ContextMenuItem>
42 <ContextMenuItem>Create Shortcut...</ContextMenuItem>
43 <ContextMenuItem>Name Window...</ContextMenuItem>
44 <ContextMenuSeparator />
45 <ContextMenuItem>Developer Tools</ContextMenuItem>
46 <ContextMenuSeparator />
47 <ContextMenuItem variant="destructive">
48 Delete
49 </ContextMenuItem>
50 </ContextMenuSubContent>
51 </ContextMenuSub>
52 <ContextMenuSeparator />
53 <ContextMenuCheckboxItem checked>
54 Show Bookmarks
55 </ContextMenuCheckboxItem>
56 <ContextMenuCheckboxItem>
57 Show Full URLs
58 </ContextMenuCheckboxItem>
59 <ContextMenuSeparator />
60 <ContextMenuRadioGroup value="pedro">
61 <ContextMenuLabel inset>People</ContextMenuLabel>
62 <ContextMenuRadioItem value="pedro">
63 Pedro Duarte
64 </ContextMenuRadioItem>
65 <ContextMenuRadioItem value="colm">
66 Colm Tuite
67 </ContextMenuRadioItem>
68 </ContextMenuRadioGroup>
69 </ContextMenuContent>
70 </ContextMenu>
71 )
72}
73Install the following dependencies:
1'use client'
2
3import { useState } from 'react'
4import Image from 'next/image'
5import { toast } from 'sonner'
6
7import { Badge } from '@/components/ui/badge'
8import {
9 ContextMenu,
10 ContextMenuCheckboxItem,
11 ContextMenuContent,
12 ContextMenuGroup,
13 ContextMenuItem,
14 ContextMenuLabel,
15 ContextMenuRadioGroup,
16 ContextMenuRadioItem,
17 ContextMenuSeparator,
18 ContextMenuSub,
19 ContextMenuSubContent,
20 ContextMenuSubTrigger,
21 ContextMenuTrigger,
22} from '@/components/ui/context-menu'
23
24type userType = {
25 id: number
26 name: string
27 image: string
28 role?: string
29 notification?: string
30}
31
32export function ContextMenuCheckable() {
33 const [users, setUsers] = useState<userType[]>([
34 {
35 id: 0,
36 name: 'Sophia Johnson',
37 image: '/images/profile1.png',
38 role: 'Admin',
39 notification: 'Email',
40 },
41 {
42 id: 1,
43 name: 'Liam Smith',
44 image: '/images/profile2.png',
45 role: 'Member',
46 notification: 'SMS',
47 },
48 {
49 id: 2,
50 name: 'Olivia Brown',
51 image: '/images/profile3.png',
52 role: 'Member',
53 notification: 'Push',
54 },
55 {
56 id: 3,
57 name: 'Noah Davis',
58 image: '/images/profile4.png',
59 role: 'Guest',
60 notification: 'Email',
61 },
62 {
63 id: 4,
64 name: 'Rafael Costa',
65 image: '/images/profile5.png',
66 role: 'Member',
67 notification: 'SMS',
68 },
69 ])
70 const [selectedUser, setSelectedUser] = useState<userType | null>(null)
71
72 const changeUserRole = (user: userType, role: string) => {
73 const updated = users.map((u) =>
74 u.id === user.id ? { ...u, role } : u,
75 )
76 setUsers(updated)
77 }
78
79 const changeNotification = (user: userType, notification: string) => {
80 const updated = users.map((u) =>
81 u.id === user.id ? { ...u, notification } : u,
82 )
83 setUsers(updated)
84 }
85
86 const getSeverity = (role: string) => {
87 if (role === 'Member') return 'default'
88 if (role === 'Guest') return 'secondary'
89 return 'outline'
90 }
91
92 return (
93 <div className="w-full max-w-md space-y-2">
94 {users.map((user: userType) => (
95 <ContextMenu key={user.id}>
96 <ContextMenuTrigger
97 onClick={() => setSelectedUser(user)}
98 className="border-border flex cursor-pointer items-center justify-between gap-4 rounded-md border p-3 hover:bg-gray-200"
99 >
100 <div className="flex items-center gap-2">
101 <Image
102 src={`${user.image}`}
103 width={32}
104 height={32}
105 alt={user.name}
106 className="rounded-full"
107 />
108 <span className="text-sm">{user.name}</span>
109 </div>
110 {user?.role && (
111 <Badge variant={getSeverity(user.role)}>
112 {user.role}
113 </Badge>
114 )}
115 </ContextMenuTrigger>
116
117 <ContextMenuContent className="">
118 <ContextMenuSub>
119 <ContextMenuSubTrigger>
120 Change Role
121 </ContextMenuSubTrigger>
122 <ContextMenuSubContent>
123 <ContextMenuGroup>
124 <ContextMenuCheckboxItem
125 checked={user.role === 'Admin'}
126 onCheckedChange={(checked) => {
127 if (checked)
128 changeUserRole(user, 'Admin')
129 }}
130 >
131 Admin
132 </ContextMenuCheckboxItem>
133 <ContextMenuCheckboxItem
134 checked={user.role === 'Member'}
135 onCheckedChange={(checked) => {
136 if (checked)
137 changeUserRole(user, 'Member')
138 }}
139 >
140 Member
141 </ContextMenuCheckboxItem>
142 <ContextMenuCheckboxItem
143 checked={user.role === 'Guest'}
144 onCheckedChange={(checked) => {
145 if (checked)
146 changeUserRole(user, 'Guest')
147 }}
148 >
149 Guest
150 </ContextMenuCheckboxItem>
151 </ContextMenuGroup>
152 </ContextMenuSubContent>
153 </ContextMenuSub>
154
155 <ContextMenuSeparator />
156
157 <ContextMenuLabel>
158 Notification Preference
159 </ContextMenuLabel>
160 <ContextMenuRadioGroup
161 value={user.notification}
162 onValueChange={(value) =>
163 changeNotification(user, value)
164 }
165 >
166 <ContextMenuRadioItem value="Email">
167 Email
168 </ContextMenuRadioItem>
169 <ContextMenuRadioItem value="SMS">
170 SMS
171 </ContextMenuRadioItem>
172 <ContextMenuRadioItem value="Push">
173 Push
174 </ContextMenuRadioItem>
175 </ContextMenuRadioGroup>
176
177 <ContextMenuSeparator />
178
179 <ContextMenuItem
180 onClick={() =>
181 toast.success(`Invitation sent to ${user.name}`)
182 }
183 >
184 Invite
185 </ContextMenuItem>
186 </ContextMenuContent>
187 </ContextMenu>
188 ))}
189 </div>
190 )
191}
192| Prop | Type | Default |
|---|---|---|
| inset | boolean | false |