# Sankey Chart

> A composable sankey diagram for visualizing flow between nodes with animated links and interactive tooltips

Source: https://ui.cortexcn.dev/docs/charts/sankey-chart

## Installation

```bash
npx shadcn@latest add @cortexcn/sankey-chart
```

## Usage

The Sankey Chart uses a composable API similar to other charts. Build diagrams by combining components:

```tsx
import {
  SankeyChart,
  SankeyNode,
  SankeyLink,
  SankeyTooltip,
} from "@/components/charts";

const data = {
  nodes: [
    { name: "Organic Search", category: "source" },
    { name: "Homepage", category: "landing" },
    { name: "Converted", category: "outcome" },
  ],
  links: [
    { source: 0, target: 1, value: 100 },
    { source: 1, target: 2, value: 80 },
  ],
};

export default function FlowDiagram() {
  return (
    <SankeyChart data={data}>
      <SankeyLink />
      <SankeyNode lineCap={4} labelOrientation="vertical" />
      <SankeyTooltip />
    </SankeyChart>
  );
}
```

## Components

### SankeyChart

The root component that computes the layout and provides context to children.

| Prop                | Type              | Default                                          | Description                            |
| ------------------- | ----------------- | ------------------------------------------------ | -------------------------------------- |
| `data`              | `SankeyData`      | required                                         | Object with `nodes` and `links` arrays |
| `margin`            | `Partial<Margin>` | `{ top: 40, right: 180, bottom: 40, left: 180 }` | Chart margins                          |
| `animationDuration` | `number`          | `1100`                                           | Animation duration in ms               |
| `aspectRatio`       | `string`          | `"2 / 1"`                                        | CSS aspect ratio                       |
| `nodeWidth`         | `number`          | `16`                                             | Width of nodes in pixels               |
| `nodePadding`       | `number`          | `24`                                             | Vertical padding between nodes         |
| `className`         | `string`          | `""`                                             | Additional CSS class                   |

### SankeyNode

Renders the nodes (bars) in the diagram with animated initialization and internal labels.

| Prop               | Type                         | Default        | Description                                                                            |
| ------------------ | ---------------------------- | -------------- | -------------------------------------------------------------------------------------- |
| `fill`             | `string`                     | -              | Fill color for all nodes                                                               |
| `lineCap`          | `number`                     | `4`            | Corner radius for nodes                                                                |
| `fadedOpacity`     | `number`                     | `0.4`          | Opacity when another element is hovered                                                |
| `showLabels`       | `boolean`                    | `true`         | Show node name labels                                                                  |
| `showValueLabels`  | `boolean`                    | `true`         | Show session count under node names                                                    |
| `labelOrientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Reading direction for outside labels. Vertical rotates labels 90° along the node edge. |
| `getNodeColor`     | `(node, index) => string`    | -              | Custom color function                                                                  |

### SankeyLink

Renders the links (flows) between nodes with animated path reveal and gradient colors.

| Prop             | Type                              | Default | Description                                          |
| ---------------- | --------------------------------- | ------- | ---------------------------------------------------- |
| `stroke`         | `string`                          | -       | Solid stroke color (overrides gradient)              |
| `strokeOpacity`  | `number`                          | `0.7`   | Link opacity                                         |
| `fadedOpacity`   | `number`                          | `0.1`   | Opacity when another element is hovered              |
| `useGradient`    | `boolean`                         | `true`  | Use gradient from source to target node color        |
| `getNodeColor`   | `(node, index) => string`         | -       | Custom node color function for gradients             |
| `getLinkColor`   | `(link, index) => string`         | -       | Custom link color (overrides gradient)               |
| `patterns`       | `ReactNode`                       | -       | Pattern definitions using `@visx/pattern` components |
| `getLinkPattern` | `(link, index) => string \| null` | -       | Return pattern ID for a link, or null for gradient   |

### SankeyTooltip

Displays tooltips for nodes and links on hover.

| Prop          | Type                   | Default          | Description                  |
| ------------- | ---------------------- | ---------------- | ---------------------------- |
| `nodeContent` | `(props) => ReactNode` | -                | Custom node tooltip renderer |
| `linkContent` | `(props) => ReactNode` | -                | Custom link tooltip renderer |
| `formatValue` | `(value) => string`    | `toLocaleString` | Value formatter              |
| `className`   | `string`               | `""`             | Additional CSS class         |

## Data Format

The sankey data follows the d3-sankey format:

```typescript
interface SankeyData {
  nodes: Array<{
    name: string;
    category: "source" | "landing" | "outcome";
    [key: string]: unknown;
  }>;
  links: Array<{
    source: number; // Index into nodes array
    target: number; // Index into nodes array
    value: number; // Flow value
  }>;
}
```

## Label orientation

Outside node labels default to horizontal text on the left or right of each column. Pass `labelOrientation="vertical"` on `SankeyNode` to rotate labels 90° along the node edge — the node name and session count stay on separate lines, stacked along the reading direction. Use wider side margins so rotated labels are not clipped.

```tsx
<SankeyChart data={data} margin={{ top: 40, right: 80, bottom: 40, left: 80 }}>
  <SankeyLink />
  <SankeyNode labelOrientation="vertical" />
  <SankeyTooltip />
</SankeyChart>
```

## Animation

The sankey chart uses the same animation system as other charts:

- **Nodes**: ScaleY and opacity fade in, staggered by index
- **Links**: Grow from source to target node using stroke-dashoffset animation
- **Gradients**: Links use gradient colors flowing from source node color to target node color
- **Easing**: `cubic-bezier(0.85, 0, 0.15, 1)` for smooth, organic motion

### Animation Timeline

- **0-600ms**: Nodes fade/scale in (staggered)
- **200-1100ms**: Links grow from source to target (staggered, starts after nodes begin appearing)

## Hover Behavior

When hovering over a node or link:

- Hovered link and its source/target nodes stay at full opacity
- Connected nodes and links remain visible
- All other links and nodes fade
- Tooltip appears showing relevant data

See the [charts gallery](https://ui.cortexcn.dev/charts/sankey-chart) for label-free layouts and pattern fills.

## Dependencies

This component requires:

```bash
pnpm add @visx/sankey @visx/responsive @visx/pattern motion react-use-measure
```