diff --git a/app/docs/components/context-menu/page.tsx b/app/docs/components/context-menu/page.tsx
new file mode 100644
index 00000000..18313984
--- /dev/null
+++ b/app/docs/components/context-menu/page.tsx
@@ -0,0 +1,91 @@
+import { Metadata } from "next"
+
+import { contextMenuMetaData } from "@/lib/metadata"
+import { Button } from "@/components/ui/8bit/button"
+import {
+ ContextMenu,
+ ContextMenuContent,
+ ContextMenuItem,
+ ContextMenuTrigger,
+} from "@/components/ui/8bit/context-menu"
+import { Separator } from "@/components/ui/separator"
+
+import CodeSnippet from "../code-snippet"
+import CopyCommandButton from "../copy-command-button"
+import InstallationCommands from "../installation-commands"
+import { OpenInV0Button } from "../open-in-v0-button"
+
+export const metadata: Metadata = {
+ title: "8-bit Context Menu",
+ description: "Displays an 8-bit context menu component.",
+ openGraph: {
+ images: contextMenuMetaData,
+ },
+}
+
+export default function ButtonPage() {
+ return (
+
+
+
Context Menu
+
+
+
+
+ Displays an 8-bit context menu component.
+
+
+
+
+
+ 8-bit context menu component
+
+
+
+
+
+
+
+
+
+
+
+
+ Profile
+ Billing
+ Team
+ Subscription
+
+
+
+
+
+
Installation
+
+
+
+
+
+
Usage
+
+
+
+
{`import { ContextMenu } from "@/components/ui/8bit/context-menu"`}
+
+
{`
+ Right click
+
+ Profile
+ Billing
+ Team
+ Subscription
+
+`}
+
+ )
+}
diff --git a/components/ui/8bit/context-menu.tsx b/components/ui/8bit/context-menu.tsx
new file mode 100644
index 00000000..47c39651
--- /dev/null
+++ b/components/ui/8bit/context-menu.tsx
@@ -0,0 +1,288 @@
+"use client"
+
+import * as React from "react"
+import { Press_Start_2P } from "next/font/google"
+import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
+import { ChevronRight, Circle } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const pressStart = Press_Start_2P({
+ weight: ["400"],
+ subsets: ["latin"],
+})
+
+const ContextMenu = ContextMenuPrimitive.Root
+
+const ContextMenuTrigger = ContextMenuPrimitive.Trigger
+
+const ContextMenuGroup = ContextMenuPrimitive.Group
+
+const ContextMenuPortal = ContextMenuPrimitive.Portal
+
+const ContextMenuSub = ContextMenuPrimitive.Sub
+
+const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup
+
+const ContextMenuSubTrigger = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, children, ...props }, ref) => (
+
+ {children}
+
+
+))
+ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName
+
+const ContextMenuSubContent = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName
+
+const ContextMenuContent = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName
+
+const ContextMenuItem = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, ...props }, ref) => (
+
+))
+ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName
+
+const ContextMenuCheckboxItem = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, checked, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+ContextMenuCheckboxItem.displayName =
+ ContextMenuPrimitive.CheckboxItem.displayName
+
+const ContextMenuRadioItem = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName
+
+const ContextMenuLabel = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, ...props }, ref) => (
+
+))
+ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName
+
+const ContextMenuSeparator = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName
+
+const ContextMenuShortcut = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => {
+ return (
+
+ )
+}
+ContextMenuShortcut.displayName = "ContextMenuShortcut"
+
+export {
+ ContextMenu,
+ ContextMenuTrigger,
+ ContextMenuContent,
+ ContextMenuItem,
+ ContextMenuCheckboxItem,
+ ContextMenuRadioItem,
+ ContextMenuLabel,
+ ContextMenuSeparator,
+ ContextMenuShortcut,
+ ContextMenuGroup,
+ ContextMenuPortal,
+ ContextMenuSub,
+ ContextMenuSubContent,
+ ContextMenuSubTrigger,
+ ContextMenuRadioGroup,
+}
diff --git a/config/nav-items.ts b/config/nav-items.ts
index 5364e7e7..589ac24c 100644
--- a/config/nav-items.ts
+++ b/config/nav-items.ts
@@ -75,6 +75,11 @@ export const navItems = {
title: "Command",
url: "/docs/components/command",
},
+ {
+ title: "Context Menu",
+ url: "/docs/components/context-menu",
+ new: true,
+ },
{
title: "Collapsible",
url: "/docs/components/collapsible",
diff --git a/lib/metadata.ts b/lib/metadata.ts
index 7e66c815..f5106a52 100644
--- a/lib/metadata.ts
+++ b/lib/metadata.ts
@@ -49,3 +49,4 @@ export const toggleGroupMetaData = "/assets/8bitcn-toggle-group-light.png"
export const accordionMetaData = "/assets/8bitcn-accordion-light.png"
export const sliderMetaData = "/assets/8bitcn-slider-light.png"
export const datePickerMetaData = "/assets/8bitcn-date-picker-light.png"
+export const contextMenuMetaData = "/assets/8bitcn-context-menu-light.png"
diff --git a/package.json b/package.json
index 66534cb7..52fca727 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"@radix-ui/react-avatar": "^1.1.9",
"@radix-ui/react-checkbox": "^1.3.1",
"@radix-ui/react-collapsible": "^1.1.10",
+ "@radix-ui/react-context-menu": "^2.2.14",
"@radix-ui/react-dialog": "^1.1.13",
"@radix-ui/react-dropdown-menu": "^2.1.14",
"@radix-ui/react-hover-card": "^1.1.13",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 995b606d..1e25d716 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -23,6 +23,9 @@ importers:
'@radix-ui/react-collapsible':
specifier: ^1.1.10
version: 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-context-menu':
+ specifier: ^2.2.14
+ version: 2.2.14(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-dialog':
specifier: ^1.1.13
version: 1.1.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -770,6 +773,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-context-menu@2.2.14':
+ resolution: {integrity: sha512-RUHvrJE2qKAd9pQ50HZZsePio4SMWEh8v6FWQwg/4t6K1fuxfb4Ec40VEVvni6V7nFxmj9srU4UZc7aYp8x0LQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-context@1.1.2':
resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
peerDependencies:
@@ -4392,6 +4408,20 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.4
+ '@radix-ui/react-context-menu@2.2.14(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0)
+ '@radix-ui/react-menu': 2.1.14(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.4
+ '@types/react-dom': 19.1.5(@types/react@19.1.4)
+
'@radix-ui/react-context@1.1.2(@types/react@19.1.4)(react@19.1.0)':
dependencies:
react: 19.1.0
diff --git a/public/assets/8bitcn-context-menu-light.png b/public/assets/8bitcn-context-menu-light.png
new file mode 100644
index 00000000..ab7c5b4a
Binary files /dev/null and b/public/assets/8bitcn-context-menu-light.png differ
diff --git a/public/r/8bit-context-menu.json b/public/r/8bit-context-menu.json
new file mode 100644
index 00000000..55cf3843
--- /dev/null
+++ b/public/r/8bit-context-menu.json
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
+ "name": "8bit-context-menu",
+ "type": "registry:component",
+ "title": "8-bit Context Menu",
+ "description": "A simple 8-bit context menu component",
+ "registryDependencies": [],
+ "files": [
+ {
+ "path": "components/ui/8bit/context-menu.tsx",
+ "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Press_Start_2P } from \"next/font/google\"\nimport * as ContextMenuPrimitive from \"@radix-ui/react-context-menu\"\nimport { ChevronRight, Circle } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst pressStart = Press_Start_2P({\n weight: [\"400\"],\n subsets: [\"latin\"],\n})\n\nconst ContextMenu = ContextMenuPrimitive.Root\n\nconst ContextMenuTrigger = ContextMenuPrimitive.Trigger\n\nconst ContextMenuGroup = ContextMenuPrimitive.Group\n\nconst ContextMenuPortal = ContextMenuPrimitive.Portal\n\nconst ContextMenuSub = ContextMenuPrimitive.Sub\n\nconst ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup\n\nconst ContextMenuSubTrigger = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef & {\n inset?: boolean\n }\n>(({ className, inset, children, ...props }, ref) => (\n \n {children}\n \n \n))\nContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName\n\nconst ContextMenuSubContent = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n))\nContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName\n\nconst ContextMenuContent = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n \n \n))\nContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName\n\nconst ContextMenuItem = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef & {\n inset?: boolean\n }\n>(({ className, inset, ...props }, ref) => (\n \n))\nContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName\n\nconst ContextMenuCheckboxItem = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef\n>(({ className, children, checked, ...props }, ref) => (\n \n \n \n \n \n \n {children}\n \n))\nContextMenuCheckboxItem.displayName =\n ContextMenuPrimitive.CheckboxItem.displayName\n\nconst ContextMenuRadioItem = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef\n>(({ className, children, ...props }, ref) => (\n \n \n \n \n \n \n {children}\n \n))\nContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName\n\nconst ContextMenuLabel = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef & {\n inset?: boolean\n }\n>(({ className, inset, ...props }, ref) => (\n \n))\nContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName\n\nconst ContextMenuSeparator = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n))\nContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName\n\nconst ContextMenuShortcut = ({\n className,\n ...props\n}: React.HTMLAttributes) => {\n return (\n \n )\n}\nContextMenuShortcut.displayName = \"ContextMenuShortcut\"\n\nexport {\n ContextMenu,\n ContextMenuTrigger,\n ContextMenuContent,\n ContextMenuItem,\n ContextMenuCheckboxItem,\n ContextMenuRadioItem,\n ContextMenuLabel,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuGroup,\n ContextMenuPortal,\n ContextMenuSub,\n ContextMenuSubContent,\n ContextMenuSubTrigger,\n ContextMenuRadioGroup,\n}\n",
+ "type": "registry:component",
+ "target": "components/ui/8bit/context-menu.tsx"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/registry.json b/registry.json
index 16a723ed..14f43b35 100644
--- a/registry.json
+++ b/registry.json
@@ -409,6 +409,20 @@
}
]
},
+ {
+ "name": "8bit-context-menu",
+ "type": "registry:component",
+ "title": "8-bit Context Menu",
+ "description": "A simple 8-bit context menu component",
+ "registryDependencies": [],
+ "files": [
+ {
+ "path": "components/ui/8bit/context-menu.tsx",
+ "type": "registry:component",
+ "target": "components/ui/8bit/context-menu.tsx"
+ }
+ ]
+ },
{
"name": "8bit-toggle",
"type": "registry:component",