# Clusters

> Clusters

- Skill: `reaviz/clusters` (Agent Skill)
- Install (CLI): `npx skillmds@latest add reaviz/clusters`
- Raw SKILL.md: https://api.skillmd.com/api/skills/reaviz/clusters/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: reaviz (https://skillmd.com/u/reaviz)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/reaviz/clusters

---

# Clusters

Group nodes into visual clusters by a shared attribute. Clusters render as rings around grouped nodes with optional labels and custom rendering.

## Configuration

Set `clusterAttribute` on `GraphCanvas` to enable clustering. The value should match a property on your `GraphNode` objects.

## GraphCanvas Cluster Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `clusterAttribute` | `string` | — | Node property to cluster by |
| `constrainDragging` | `boolean` | `false` | Constrain node dragging to cluster bounds |
| `onRenderCluster` | `ClusterRenderer` | — | Custom cluster render function |
| `onClusterClick` | `(cluster, event) => void` | — | Cluster click handler |
| `onClusterPointerOver` | `(cluster, event) => void` | — | Pointer enters cluster |
| `onClusterPointerOut` | `(cluster, event) => void` | — | Pointer leaves cluster |
| `onClusterDragged` | `(cluster) => void` | — | Cluster drag completed |

## ClusterRenderer Props

```ts
interface ClusterRendererProps {
  outerRadius: number;
  innerRadius: number;
  padding: number;
  opacity: number;
  label: ClusterLabel;
  theme: Theme;
}
```

## Basic Clustering

```tsx
const nodes = [
  { id: '1', label: 'Web Server', cluster: 'frontend' },
  { id: '2', label: 'API Server', cluster: 'backend' },
  { id: '3', label: 'Database', cluster: 'backend' },
  { id: '4', label: 'CDN', cluster: 'frontend' },
];

<GraphCanvas
  nodes={nodes}
  edges={edges}
  clusterAttribute="cluster"
/>
```

## Cluster Events

```tsx
<GraphCanvas
  nodes={nodes}
  edges={edges}
  clusterAttribute="cluster"
  onClusterClick={(cluster, event) => console.log(cluster)}
/>
```

## Custom Cluster Renderer

```tsx
import { GraphCanvas, ClusterRendererProps } from 'reagraph';

const renderCluster = ({ outerRadius, label, theme }: ClusterRendererProps) => (
  <mesh>
    <ringGeometry args={[outerRadius - 2, outerRadius, 32]} />
    <meshBasicMaterial color="#4CAF50" opacity={0.3} transparent />
  </mesh>
);

<GraphCanvas
  nodes={nodes}
  edges={edges}
  clusterAttribute="cluster"
  onRenderCluster={renderCluster}
/>
```

## Cluster Theme Styling

```tsx
const customTheme = {
  ...lightTheme,
  cluster: {
    stroke: '#D8E6EA',
    fill: '#f0f0f0',
    opacity: 1,
    selectedOpacity: 1,
    inactiveOpacity: 0.1,
    label: {
      color: '#2A6475',
      stroke: '#fff',
    },
  },
};
```

## Notes

- Clusters are automatically computed from the `clusterAttribute` value on each node
- Nodes without the attribute are not clustered
- Cluster-aware layouts (e.g. `forceDirected2d`) group nodes spatially
- `constrainDragging` keeps dragged nodes within their cluster boundary
- Default cluster rendering is a ring with a label; use `onRenderCluster` to customize

