Radial Menu
animate-ui/components-community-radial-menuFreeComponent
A circular context menu built with Base UI, displaying actions in a clean radial layout with full keyboard support and smooth interaction.
Install it
npx shadcn@latest add https://animate-ui.com/r/components-community-radial-menu.jsonSource
1'use client';23import * as React from 'react';4import { LucideIcon } from 'lucide-react';5import { motion, AnimatePresence, type Transition } from 'motion/react';6import { ContextMenu } from '@base-ui-components/react/context-menu';7import { cn } from '@/lib/utils';89type RadialMenuProps = {10 children?: React.ReactNode;11 menuItems: MenuItem[];12 size?: number;13 iconSize?: number;14 bandWidth?: number;15 innerGap?: number;16 outerGap?: number;17 outerRingWidth?: number;18 onSelect?: (item: MenuItem) => void;19};2021type MenuItem = {22 id: number;23 label: string;24 icon: LucideIcon;25};2627type Point = { x: number; y: number };2829const menuTransition: Transition = {30 type: 'spring',31 stiffness: 420,32 damping: 32,33 mass: 1,34};3536const wedgeTransition: Transition = {37 duration: 0.05,38 ease: 'easeOut',39};4041const FULL_CIRCLE = 360;42const START_ANGLE = -90;4344function degToRad(deg: number) {45 return (deg * Math.PI) / 180;46}4748function polarToCartesian(radius: number, angleDeg: number): Point {49 const rad = degToRad(angleDeg);50 return {51 x: Math.cos(rad) * radius,52 y: Math.sin(rad) * radius,53 };54}5556function slicePath(57 index: number,58 total: number,59 wedgeRadius: number,60 innerRadius: number,61) {62 if (total <= 0) return '';6364 // single item → full donut ring65 if (total === 1) {66 return `67 M ${wedgeRadius} 068 A ${wedgeRadius} ${wedgeRadius} 0 1 1 ${-wedgeRadius} 069 A ${wedgeRadius} ${wedgeRadius} 0 1 1 ${wedgeRadius} 070 M ${innerRadius} 071 A ${innerRadius} ${innerRadius} 0 1 0 ${-innerRadius} 072 A ${innerRadius} ${innerRadius} 0 1 0 ${innerRadius} 073 `;74 }7576 const anglePerSlice = FULL_CIRCLE / total;77 const midDeg = START_ANGLE + anglePerSlice * index;78 const halfSlice = anglePerSlice / 2;7980 const startDeg = midDeg - halfSlice;81 const endDeg = midDeg + halfSlice;8283 const outerStart = polarToCartesian(wedgeRadius, startDeg);84 const outerEnd = polarToCartesian(wedgeRadius, endDeg);85 const innerStart = polarToCartesian(innerRadius, startDeg);86 const innerEnd = polarToCartesian(innerRadius, endDeg);8788 const largeArcFlag = anglePerSlice > 180 ? 1 : 0;8990 return `91 M ${outerStart.x} ${outerStart.y}92 A ${wedgeRadius} ${wedgeRadius} 0 ${largeArcFlag} 1 ${outerEnd.x} ${outerEnd.y}93 L ${innerEnd.x} ${innerEnd.y}94 A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${innerStart.x} ${innerStart.y}95 Z96 `;97}9899function RadialMenu({100 children,101 menuItems,102 size = 240,103 iconSize = 18,104// … truncated
What it pulls in
npm packages
Files it writes
More from Animate UI
All 580 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Avatar Groupcomponents-animate-avatar-group | Animate UI | Components | Free | 1 dep | |
| Codecomponents-animate-code | Animate UI | Components | Free | 1 dep | |
| Code Tabscomponents-animate-code-tabs | Animate UI | Components | Free | 1 dep | |
| Cursorcomponents-animate-cursor | Animate UI | Components | Free | no deps | |
| GitHub Stars Wheelcomponents-animate-github-stars-wheel | Animate UI | Components | Free | 1 dep | |
| Tabscomponents-animate-tabs | Animate UI | Components | Free | no deps | |
| Tooltipcomponents-animate-tooltip | Animate UI | Components | Free | 1 dep | |
| Bubble Backgroundcomponents-backgrounds-bubble | Animate UI | Components | Free | 1 dep |
