Cards & Grids

Orbiting Circles

A ring of icons or avatars circling a round orbit, stackable into multiple orbits with reverse directions and different speeds.

A ring of icons or avatars spaced evenly along a circular orbit. Several instances can be stacked into inner and outer orbits, each with its own radius, speed, and direction — a common way to express "a technology ecosystem around a core" or a set of integrations.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add orbiting-circles.en

New project or installation trouble? Start here →

Orbiting Circles is a React orbit animation component. The orbit runs on CSS @keyframes driven by CSS variables, the orbit line is SVG, styling is Tailwind CSS, and Motion is used only for its useReducedMotion hook.

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

100
20
40
0
Active edited usageDefault props
<OrbitingCircles/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

When to use

  • The integrations section of a SaaS homepage: your logo in the middle, the services you connect to circling around it.
  • Architecture or ecosystem diagrams, such as a core API surrounded by SDKs, databases, and cloud services.
  • Community or team pages, using a ring of avatars to show members.
  • Not a good fit: showing dozens of icons at once makes a single orbit crowded; use Icon Cloud instead.

Installation

npx webberui@latest add https://webberui.com/r/orbiting-circles.en.json

Or, once registries are configured in components.json, install it as @webberui/orbiting-circles.

Usage

children are distributed evenly around the circumference. Stack two instances to build inner and outer orbits, using reverse to send the outer ring the other way and startAngle to stagger the nodes.

import { OrbitingCircles } from "@/components/ui/orbiting-circles";

<div className="relative flex h-[360px] items-center justify-center">
  <OrbitingCircles radius={62} duration={16} iconSize={34} className="absolute">
    <Github />
    <Twitter />
    <Figma />
  </OrbitingCircles>

  <OrbitingCircles
    radius={122}
    duration={28}
    iconSize={42}
    reverse
    startAngle={20}
    className="absolute"
  >
    <Chrome />
    <Slack />
    <Cloud />
    <Database />
  </OrbitingCircles>
</div>

Examples

Center logo with one orbit

Center a relative container, put the product logo in the middle, and layer OrbitingCircles on top with absolute.

import { Cloud, Database, Mail, Server } from "lucide-react";
import { OrbitingCircles } from "@/components/ui/orbiting-circles";

export function IntegrationsOrbit() {
  return (
    <div className="relative flex h-[300px] items-center justify-center">
      <div className="flex size-16 items-center justify-center rounded-2xl bg-foreground text-sm font-semibold text-background">
        API
      </div>
      <OrbitingCircles radius={110} duration={24} iconSize={44} className="absolute">
        <Database className="size-6" />
        <Cloud className="size-6" />
        <Server className="size-6" />
        <Mail className="size-6" />
      </OrbitingCircles>
    </div>
  );
}

Avatar ring without the orbit line

path={false} removes the dashed orbit, and children can be any element, such as round avatar images.

const members = ["/avatars/1.jpg", "/avatars/2.jpg", "/avatars/3.jpg", "/avatars/4.jpg", "/avatars/5.jpg"];

<OrbitingCircles radius={90} iconSize={48} duration={30} path={false}>
  {members.map((src) => (
    <img key={src} src={src} alt="" className="size-12 rounded-full object-cover ring-2 ring-background" />
  ))}
</OrbitingCircles>

Interleave two sets on the same radius with startAngle

When two instances share a radius and direction, set the second one's startAngle to half the gap between nodes and the two sets alternate.

<div className="relative flex h-[260px] items-center justify-center">
  <OrbitingCircles radius={100} duration={20} className="absolute">
    <Cloud />
    <Server />
    <Database />
  </OrbitingCircles>
  <OrbitingCircles radius={100} duration={20} startAngle={60} path={false} className="absolute">
    <Mail />
    <Cloud />
    <Server />
  </OrbitingCircles>
</div>

Props

PropTypeDefaultDescription
childrenReact.ReactNode—Elements circling the orbit, distributed evenly around the circumference
radiusnumber100Orbit radius (px)
durationnumber20Duration of one full lap (seconds); larger is slower
reversebooleanfalseOrbit in reverse (counter-clockwise)
iconSizenumber40Edge length of a single element (px)
startAnglenumber0Starting angle of the first element (degrees)
pathbooleantrueShow the faint circular orbit
classNamestring—Forwarded to the outermost container

Accessibility

  • When the user has "reduce motion" enabled at the system level, the elements land at their static positions on the orbit with no circling animation
  • The orbit path is marked aria-hidden and is decorative only

FAQ

Does it work with the Next.js App Router?

Yes. The source already has "use client", so import it from a Server Component page directly. The first render matches the server output and only switches to the static version after mount when reduced motion is on, so there is no hydration mismatch.

How do I change the speed and size?

duration is the number of seconds per revolution (higher is slower), radius sets the orbit radius, and iconSize sets the box size of each item. The component's outer size is radius * 2 + iconSize, which is useful when reserving layout space.

Is it expensive to run?

The orbit is a CSS @keyframes transform animation, so there is no per-frame JavaScript and no requestAnimationFrame loop.

How do I change the orbit line color?

The orbit line is an SVG <circle> styled with the stroke-black/10 dark:stroke-white/15 classes in the source; edit them in your copied file, or pass path={false} to hide the line.

What happens when the user prefers reduced motion?

Items sit at fixed positions on the orbit with no circling animation.