Radial menu
A radial menu for React, the game weapon wheel: choices around a ring, picked by the pointer's direction or the arrow keys.
Install
# once per project: where @zeroui items come frompnpm dlx shadcn@latest registry add @zeroui=https://zeroui.catforest.dev/r/{name}.jsonpnpm dlx shadcn@latest add @zeroui/radial-menuExamples
A quick-use wheel
Point anywhere, then click; or use the arrows and Enter.
import { useState } from "react";import { CellIcon } from "@/components/zero/icons/cell";import { ChipIcon } from "@/components/zero/icons/chip";import { CrateIcon } from "@/components/zero/icons/crate";import { FlareIcon } from "@/components/zero/icons/flare";import { KeyIcon } from "@/components/zero/icons/key";import { KitIcon } from "@/components/zero/icons/kit";import { RadialMenu, RadialMenuItem } from "@/components/zero/radial-menu";const wheel = [ { value: "kit", label: "Repair kit", icon: KitIcon }, { value: "cell", label: "Power cell", icon: CellIcon }, { value: "flare", label: "Signal flare", icon: FlareIcon }, { value: "chip", label: "Route chip", icon: ChipIcon }, { value: "key", label: "Gate key", icon: KeyIcon }, { value: "crate", label: "Drop the crate", icon: CrateIcon, disabled: true },];export default function RadialMenuQuick() { const [used, setUsed] = useState<string | null>(null); return ( <div className="grid justify-items-center gap-4"> <RadialMenu aria-label="Quick use" onSelect={setUsed}> {wheel.map((item) => ( <RadialMenuItem key={item.value} value={item.value} label={item.label} disabled={item.disabled} > <item.icon /> </RadialMenuItem> ))} </RadialMenu> <p className="font-medium font-zero-text text-zero-sm text-zero-text-2"> {used ? `Used: ${wheel.find((item) => item.value === used)?.label}` : "Point anywhere, then click; or use the arrows and Enter."} </p> </div> );}Behaviour
Up to eight items around a ring, the first at the top, going clockwise. Pointing anywhere picks the item in that direction, so a flick of the mouse is enough; a bright wedge swings to it and the centre names it. A click or Enter chooses it.
The arrow keys go round the ring. Focus stays on the wheel and the picked item is its active descendant, so screen readers hear each as it is reached. It is the wheel only: show it where and when you need it, on a held key or in an overlay.
Props
RadialMenu
| Prop | Type | Default | Description |
|---|---|---|---|
| onSelect | (value: string) => void | — | When an item is chosen. |
| defaultValue | string | — | The item picked when it opens. |
RadialMenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | — | What onSelect gets. |
| label | string | — | Its name: said by screen readers and shown in the centre. |
| disabled | boolean | — | Shown dim; cannot be chosen. |