1import { createSignal } from "solid-js";2
3import {4 ContextMenu,5 ContextMenuCheckboxItem,6 ContextMenuContent,7 ContextMenuGroup,8 ContextMenuGroupLabel,9 ContextMenuItem,10 ContextMenuPortal,11 ContextMenuRadioGroup,12 ContextMenuRadioItem,13 ContextMenuSeparator,14 ContextMenuShortcut,15 ContextMenuSub,16 ContextMenuSubContent,17 ContextMenuSubTrigger,18 ContextMenuTrigger,19} from "~/components/ui/context-menu";20
21export default function ContextMenuDemo() {22 const [showGitLog, setShowGitLog] = createSignal(true);23 const [showHistory, setShowHistory] = createSignal(false);24 const [branch, setBranch] = createSignal("main");25
26 return (27 <ContextMenu>28 <ContextMenuTrigger class="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm">29 Right click here.30 </ContextMenuTrigger>31 <ContextMenuPortal>32 <ContextMenuContent class="w-48">33 <ContextMenuItem>34 <span>Commit</span>35 <ContextMenuShortcut>⌘+K</ContextMenuShortcut>36 </ContextMenuItem>37 <ContextMenuItem>38 <span>Push</span>39 <ContextMenuShortcut>⇧+⌘+K</ContextMenuShortcut>40 </ContextMenuItem>41 <ContextMenuItem disabled>42 <span>Update Project</span>43 <ContextMenuShortcut>⌘+T</ContextMenuShortcut>44 </ContextMenuItem>45 <ContextMenuSub overlap>46 <ContextMenuSubTrigger>GitHub</ContextMenuSubTrigger>47 <ContextMenuPortal>48 <ContextMenuSubContent>49 <ContextMenuItem>Create Pull Request…</ContextMenuItem>50 <ContextMenuItem>View Pull Requests</ContextMenuItem>51 <ContextMenuItem>Sync Fork</ContextMenuItem>52 <ContextMenuSeparator />53 <ContextMenuItem>Open on GitHub</ContextMenuItem>54 </ContextMenuSubContent>55 </ContextMenuPortal>56 </ContextMenuSub>57 <ContextMenuSeparator />58 <ContextMenuCheckboxItem59 checked={showGitLog()}60 onChange={setShowGitLog}61 >62 Show Git Log63 </ContextMenuCheckboxItem>64 <ContextMenuCheckboxItem65 checked={showHistory()}66 onChange={setShowHistory}67 >68 Show History69 </ContextMenuCheckboxItem>70 <ContextMenuSeparator />71 <ContextMenuGroup>72 <ContextMenuGroupLabel>Branches</ContextMenuGroupLabel>73 <ContextMenuRadioGroup value={branch()} onChange={setBranch}>74 <ContextMenuRadioItem value="main">main</ContextMenuRadioItem>75 <ContextMenuRadioItem value="develop">76 develop77 </ContextMenuRadioItem>78 </ContextMenuRadioGroup>79 </ContextMenuGroup>80 </ContextMenuContent>81 </ContextMenuPortal>82 </ContextMenu>83 );84}
npx shadcn@latest add https://solid-ui-neobrutalism.vercel.app/r/context-menu.json
yarn shadcn@latest add https://solid-ui-neobrutalism.vercel.app/r/context-menu.json
pnpm dlx shadcn@latest add https://solid-ui-neobrutalism.vercel.app/r/context-menu.json
bunx --bun shadcn@latest add https://solid-ui-neobrutalism.vercel.app/r/context-menu.json
Install the following dependencies
npm install @kobalte/core
yarn add @kobalte/core
pnpm add @kobalte/core
bun add @kobalte/core
Copy and paste the following code into your project
1import type { PolymorphicProps } from "@kobalte/core/polymorphic";2import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js";3
4import { splitProps } from "solid-js";5import * as ContextMenuPrimitive from "@kobalte/core/context-menu";6import CheckIcon from "lucide-solid/icons/check";7import ChevronRightIcon from "lucide-solid/icons/chevron-right";8import DotIcon from "lucide-solid/icons/dot";9
10import { cn } from "~/lib/utils";11
12const ContextMenuTrigger = <T extends ValidComponent = "div">(13 props: PolymorphicProps<T, ContextMenuPrimitive.ContextMenuTriggerProps<T>>,14) => {15 return (16 <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} />17 );18};19
20const ContextMenuPortal = (21 props: ContextMenuPrimitive.ContextMenuPortalProps,22) => {23 return (24 <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />25 );26};27
28const ContextMenuSub = (props: ContextMenuPrimitive.ContextMenuSubProps) => {29 return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />;30};31
32const ContextMenuGroup = <T extends ValidComponent = "div">(33 props: PolymorphicProps<T, ContextMenuPrimitive.ContextMenuGroupProps<T>>,34) => {35 return (36 <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />37 );38};39
40const ContextMenuRadioGroup = <41 TValue = string,42 T extends ValidComponent = "div",43>(44 props: PolymorphicProps<45 T,46 ContextMenuPrimitive.ContextMenuRadioGroupProps<T, TValue>47 >,48) => {49 return (50 <ContextMenuPrimitive.RadioGroup51 data-slot="context-menu-radio-group"52 {...props}53 />54 );55};56
57const ContextMenu: Component<ContextMenuPrimitive.ContextMenuRootProps> = (58 props,59) => {60 return (61 <ContextMenuPrimitive.Root data-slot="context-menu" gutter={4} {...props} />62 );63};64
65type ContextMenuContentProps<T extends ValidComponent = "div"> =66 ContextMenuPrimitive.ContextMenuContentProps<T> & {67 class?: string | undefined;68 };69
70const ContextMenuContent = <T extends ValidComponent = "div">(71 props: PolymorphicProps<T, ContextMenuContentProps<T>>,72) => {73 const [local, others] = splitProps(props as ContextMenuContentProps, [74 "class",75 ]);76 return (77 <ContextMenuPrimitive.Portal>78 <ContextMenuPrimitive.Content79 data-slot="context-menu-content"80 class={cn(81 "z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] animate-in overflow-hidden rounded-md border-2 bg-background p-1 text-foreground",82 local.class,83 )}84 {...others}85 />86 </ContextMenuPrimitive.Portal>87 );88};89
90type ContextMenuItemProps<T extends ValidComponent = "div"> =91 ContextMenuPrimitive.ContextMenuItemProps<T> & {92 class?: string | undefined;93 };94
95const ContextMenuItem = <T extends ValidComponent = "div">(96 props: PolymorphicProps<T, ContextMenuItemProps<T>>,97) => {98 const [local, others] = splitProps(props as ContextMenuItemProps, ["class"]);99 return (100 <ContextMenuPrimitive.Item101 data-slot="context-menu-item"102 class={cn(103 "relative flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm transition-colors outline-none select-none focus:bg-primary focus:text-primary-foreground ui-disabled:pointer-events-none ui-disabled:opacity-50",104 local.class,105 )}106 {...others}107 />108 );109};110
111const ContextMenuShortcut: Component<ComponentProps<"span">> = (props) => {112 const [local, others] = splitProps(props, ["class"]);113 return (114 <span115 data-slot="context-menu-shortcut"116 class={cn("ml-auto text-xs tracking-widest opacity-60", local.class)}117 {...others}118 />119 );120};121
122type ContextMenuSeparatorProps<T extends ValidComponent = "hr"> =123 ContextMenuPrimitive.ContextMenuSeparatorProps<T> & {124 class?: string | undefined;125 };126
127const ContextMenuSeparator = <T extends ValidComponent = "hr">(128 props: PolymorphicProps<T, ContextMenuSeparatorProps<T>>,129) => {130 const [local, others] = splitProps(props as ContextMenuSeparatorProps, [131 "class",132 ]);133 return (134 <ContextMenuPrimitive.Separator135 data-slot="context-menu-separator"136 class={cn("-mx-1 my-1 h-0.5 bg-border", local.class)}137 {...others}138 />139 );140};141
142type ContextMenuSubTriggerProps<T extends ValidComponent = "div"> =143 ContextMenuPrimitive.ContextMenuSubTriggerProps<T> & {144 class?: string | undefined;145 children?: JSX.Element;146 };147
148const ContextMenuSubTrigger = <T extends ValidComponent = "div">(149 props: PolymorphicProps<T, ContextMenuSubTriggerProps<T>>,150) => {151 const [local, others] = splitProps(props as ContextMenuSubTriggerProps, [152 "class",153 "children",154 ]);155 return (156 <ContextMenuPrimitive.SubTrigger157 data-slot="context-menu-sub-trigger"158 class={cn(159 "flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none focus:bg-primary focus:text-primary-foreground ui-expanded:bg-primary ui-expanded:text-primary-foreground",160 local.class,161 )}162 {...others}163 >164 {local.children}165 <ChevronRightIcon class="ml-auto size-4" />166 </ContextMenuPrimitive.SubTrigger>167 );168};169
170type ContextMenuSubContentProps<T extends ValidComponent = "div"> =171 ContextMenuPrimitive.ContextMenuSubContentProps<T> & {172 class?: string | undefined;173 };174
175const ContextMenuSubContent = <T extends ValidComponent = "div">(176 props: PolymorphicProps<T, ContextMenuSubContentProps<T>>,177) => {178 const [local, others] = splitProps(props as ContextMenuSubContentProps, [179 "class",180 ]);181 return (182 <ContextMenuPrimitive.SubContent183 data-slot="context-menu-sub-content"184 class={cn(185 "z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] animate-in overflow-hidden rounded-md border-2 bg-background p-1 text-foreground",186 local.class,187 )}188 {...others}189 />190 );191};192
193type ContextMenuCheckboxItemProps<T extends ValidComponent = "div"> =194 ContextMenuPrimitive.ContextMenuCheckboxItemProps<T> & {195 class?: string | undefined;196 children?: JSX.Element;197 };198
199const ContextMenuCheckboxItem = <T extends ValidComponent = "div">(200 props: PolymorphicProps<T, ContextMenuCheckboxItemProps<T>>,201) => {202 const [local, others] = splitProps(props as ContextMenuCheckboxItemProps, [203 "class",204 "children",205 ]);206 return (207 <ContextMenuPrimitive.CheckboxItem208 data-slot="context-menu-checkbox-item"209 class={cn(210 "relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm transition-colors outline-none select-none focus:bg-primary focus:text-primary-foreground ui-disabled:pointer-events-none ui-disabled:opacity-50",211 local.class,212 )}213 {...others}214 >215 <span class="absolute left-2 flex size-3.5 items-center justify-center">216 <ContextMenuPrimitive.ItemIndicator>217 <CheckIcon class="size-4" />218 </ContextMenuPrimitive.ItemIndicator>219 </span>220 {local.children}221 </ContextMenuPrimitive.CheckboxItem>222 );223};224
225type ContextMenuGroupLabelProps<T extends ValidComponent = "span"> =226 ContextMenuPrimitive.ContextMenuGroupLabelProps<T> & {227 class?: string | undefined;228 };229
230const ContextMenuGroupLabel = <T extends ValidComponent = "span">(231 props: PolymorphicProps<T, ContextMenuGroupLabelProps<T>>,232) => {233 const [local, others] = splitProps(props as ContextMenuGroupLabelProps, [234 "class",235 ]);236 return (237 <ContextMenuPrimitive.GroupLabel238 data-slot="context-menu-group-label"239 class={cn("px-2 py-1.5 text-sm font-semibold", local.class)}240 {...others}241 />242 );243};244
245type ContextMenuRadioItemProps<T extends ValidComponent = "div"> =246 ContextMenuPrimitive.ContextMenuRadioItemProps<T> & {247 class?: string | undefined;248 children?: JSX.Element;249 };250
251const ContextMenuRadioItem = <T extends ValidComponent = "div">(252 props: PolymorphicProps<T, ContextMenuRadioItemProps<T>>,253) => {254 const [local, others] = splitProps(props as ContextMenuRadioItemProps, [255 "class",256 "children",257 ]);258 return (259 <ContextMenuPrimitive.RadioItem260 data-slot="context-menu-radio-item"261 class={cn(262 "relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm transition-colors outline-none select-none focus:bg-primary focus:text-primary-foreground ui-disabled:pointer-events-none ui-disabled:opacity-50",263 local.class,264 )}265 {...others}266 >267 <span class="absolute left-2 flex size-3.5 items-center justify-center">268 <ContextMenuPrimitive.ItemIndicator>269 <DotIcon class="size-10" />270 </ContextMenuPrimitive.ItemIndicator>271 </span>272 {local.children}273 </ContextMenuPrimitive.RadioItem>274 );275};276
277export {278 ContextMenu,279 ContextMenuTrigger,280 ContextMenuPortal,281 ContextMenuContent,282 ContextMenuItem,283 ContextMenuShortcut,284 ContextMenuSeparator,285 ContextMenuSub,286 ContextMenuSubTrigger,287 ContextMenuSubContent,288 ContextMenuCheckboxItem,289 ContextMenuGroup,290 ContextMenuGroupLabel,291 ContextMenuRadioGroup,292 ContextMenuRadioItem,293};
Update the import paths to match your project setup.
1import {2 ContextMenu,3 ContextMenuCheckboxItem,4 ContextMenuContent,5 ContextMenuGroup,6 ContextMenuGroupLabel,7 ContextMenuItem,8 ContextMenuPortal,9 ContextMenuRadioGroup,10 ContextMenuRadioItem,11 ContextMenuSeparator,12 ContextMenuShortcut,13 ContextMenuSub,14 ContextMenuSubContent,15 ContextMenuSubTrigger,16 ContextMenuTrigger,17} from "~/components/ui/context-menu";
1<ContextMenu>2 <ContextMenuTrigger>Open</ContextMenuTrigger>3 <ContextMenuContent>4 <ContextMenuLabel>My Account</ContextMenuLabel>5 <ContextMenuSeparator />6 <ContextMenuItem>Profile</ContextMenuItem>7 <ContextMenuItem>Billing</ContextMenuItem>8 <ContextMenuItem>Team</ContextMenuItem>9 <ContextMenuItem>Subscription</ContextMenuItem>10 </ContextMenuContent>11</ContextMenu>