Orbiting Elements
ui-beats/orbiting-elementsFreeComponent
The OrbitingElements component places children evenly around a circle and rotates them around a centre point, keeping each one upright.
Install it
npx shadcn@latest add https://uibeats.com/r/orbiting-elements.jsonSource
1"use client";23import { motion, useReducedMotion } from "motion/react";4import { Children, type ReactNode } from "react";56interface OrbitingElementsProps {7 children: ReactNode;8 /** Orbit radius in pixels. */9 radius?: number;10 /** Seconds for one full revolution. */11 duration?: number;12 reverse?: boolean;13 /** Draw the orbit path itself. */14 showPath?: boolean;15 /** Degrees to offset the first item by. */16 startAngle?: number;17 className?: string;18}1920/**21 * Places children evenly around a circle and rotates them around a centre22 * point.23 *24 * Each item sits on a rotating arm and counter-rotates at the same speed, so25 * it travels the orbit while staying upright — icons and text never end up26 * upside down at the bottom of the circle.27 */28export function OrbitingElements({29 children,30 radius = 100,31 duration = 20,32 reverse = false,33 showPath = true,34 startAngle = 0,35 className = "",36}: OrbitingElementsProps) {37 const items = Children.toArray(children);38 const prefersReducedMotion = useReducedMotion();39 const spin = reverse ? -360 : 360;4041 const transition = {42 duration,43 repeat: Infinity,44 ease: "linear" as const,45 };4647 return (48 <div49 className={`relative ${className}`}50 style={{ width: radius * 2, height: radius * 2 }}51 >52 {showPath ? (53 <div54 aria-hidden="true"55 className="absolute inset-0 rounded-full border border-dashed border-border"56 />57 ) : null}5859 {items.map((item, index) => {60 const angle = startAngle + (360 / items.length) * index;6162 return (63 <motion.div64 key={index}65 className="absolute top-1/2 left-1/2 size-0"66 // The arm: rotates around the centre, carrying the item at67 // `radius` along its length.68 initial={{ rotate: angle }}69 animate={70 prefersReducedMotion ? undefined : { rotate: angle + spin }71 }72 transition={transition}73 >74 {/* `w-max` on both wrappers matters: the arm above is `size-0` so75 the orbit pivots on a point, and without it these inherit a76 zero-width containing block and squash the item into a sliver. */}77 <div78 className="w-max"79 style={{ transform: `translateX(${radius}px)` }}80 >81 <motion.div82 // Counter-rotation, same duration and easing, so the item83// … truncated
What it pulls in
npm packages
Files it writes
More from ui-beats
All 26 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Animated Beamanimated-beam | ui-beats | Components | Free | 1 dep | |
| Bouncebounce | ui-beats | Components | Free | 1 dep | |
| Dockdock | ui-beats | Components | Free | 1 dep | |
| Fade Infade-in | ui-beats | Components | Free | 1 dep | |
| Fade In Unblurfade-in-unblur | ui-beats | Components | Free | 1 dep | |
| Flip Cardflip-card | ui-beats | Components | Free | 2 deps | |
| Glowing Cardglowing-card | ui-beats | Components | Free | 1 dep | |
| Gradient Flowgradient-flow | ui-beats | Components | Free | 1 dep |
