home / skills / dennisadriaans / vue-chrts / dagre-graph

This skill helps you create clear hierarchical diagrams with DagreGraph, enabling efficient org charts, dependency trees, and flowcharts.

npx playbooks add skill dennisadriaans/vue-chrts --skill dagre-graph

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
9.0 KB
---
name: dagre-graph
description: Build DagreGraph components for hierarchical diagrams. Use for org charts, dependency graphs, flowcharts, and decision trees.
---

# DagreGraph

DagreGraph renders directed acyclic graphs with automatic hierarchical layout. Perfect for org charts, dependency trees, system architectures, and flowcharts.

## Mental Model

```
┌─────────────────────────────────────────────────────────────┐
│  HIERARCHICAL LAYOUT                                        │
│                                                             │
│  TB (Top-Bottom)        LR (Left-Right)                     │
│       ┌───┐                                                 │
│       │ A │             ┌───┐   ┌───┐   ┌───┐              │
│       └─┬─┘             │ A │───│ B │───│ D │              │
│     ┌───┴───┐           └───┘   └─┬─┘   └───┘              │
│   ┌─┴─┐   ┌─┴─┐                   │                        │
│   │ B │   │ C │                 ┌─┴─┐                      │
│   └─┬─┘   └───┘                 │ C │                      │
│   ┌─┴─┐                         └───┘                      │
│   │ D │                                                    │
│   └───┘                                                    │
│                                                             │
│  Nodes + Links = Graph                                      │
└─────────────────────────────────────────────────────────────┘
```

## Data Structure

```typescript
interface GraphNodeDatum {
  id: string        // unique identifier
  label?: string    // display text
  level?: number    // optional hierarchy level
  [key: string]: any // custom data
}

interface GraphLinkDatum {
  source: string    // source node id
  target: string    // target node id
  label?: string    // optional link label
  [key: string]: any
}

const data = {
  nodes: [...],
  links: [...]
}
```

## Complete Example

```vue
<script setup lang="ts">
import { DagreGraph, LegendPosition, type GraphNodeDatum, type GraphLinkDatum } from 'vue-chrts'

const orgData = {
  nodes: [
    { id: 'ceo', label: 'CEO', level: 0 },
    { id: 'cto', label: 'CTO', level: 1 },
    { id: 'cfo', label: 'CFO', level: 1 },
    { id: 'eng-lead', label: 'Eng Lead', level: 2 },
    { id: 'design-lead', label: 'Design Lead', level: 2 },
    { id: 'team-a', label: 'Team A', level: 3 },
    { id: 'team-b', label: 'Team B', level: 3 },
  ] as GraphNodeDatum[],
  links: [
    { source: 'ceo', target: 'cto' },
    { source: 'ceo', target: 'cfo' },
    { source: 'cto', target: 'eng-lead' },
    { source: 'cto', target: 'design-lead' },
    { source: 'eng-lead', target: 'team-a' },
    { source: 'eng-lead', target: 'team-b' },
  ] as GraphLinkDatum[],
}

const levelColors: Record<number, string> = {
  0: '#8b5cf6', // CEO - Purple
  1: '#3b82f6', // C-level - Blue
  2: '#10b981', // Leads - Green
  3: '#f59e0b', // Teams - Orange
}

const getNodeColor = (node: GraphNodeDatum) => {
  return levelColors[node.level ?? 0] || '#6b7280'
}

const handleNodeClick = (node: GraphNodeDatum, event?: MouseEvent) => {
  console.log('Node clicked:', node)
}
</script>

<template>
  <DagreGraph
    :data="orgData"
    :height="500"
    :nodeLabel="(n) => n.label || n.id"
    :nodeFillColor="getNodeColor"
    :nodeSize="50"
    :dagreSettings="{
      rankdir: 'TB',
      nodesep: 60,
      ranksep: 80
    }"
    :linkArrows="'end'"
    @node:click="handleNodeClick"
  />
</template>
```

## Key Props Reference

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `data` | `GraphData<N, L>` | required | Nodes and links data |
| `height` | `number` | `600` | Chart height in pixels |
| `width` | `number` | auto | Chart width in pixels |
| `nodeLabel` | `(node: N) => string` | - | Node label accessor |
| `nodeSubLabel` | `(node: N) => string` | - | Node sub-label accessor |
| `nodeFillColor` | `(node: N) => string` | - | Node background color |
| `nodeStrokeColor` | `(node: N) => string` | - | Node border color |
| `nodeSize` | `number \| (node: N) => number` | `40` | Node size in pixels |
| `nodeShape` | `NodeShape \| (node: N) => NodeShape` | `'circle'` | circle/square/triangle/diamond |
| `linkColor` | `(link: L) => string` | - | Link stroke color |
| `linkWidth` | `(link: L) => number` | - | Link stroke width |
| `linkArrows` | `LinkArrowPosition` | `'none'` | start/end/both/none |
| `dagreSettings` | `DagreLayoutSettings` | - | Layout algorithm config |
| `categories` | `Record<string, BulletLegendItem>` | - | Legend categories |
| `legendPosition` | `LegendPosition` | - | Legend placement |
| `disableZoom` | `boolean` | `false` | Disable pan/zoom |
| `disableDrag` | `boolean` | `false` | Disable node dragging |

## Layout Settings (dagreSettings)

```typescript
interface DagreLayoutSettings {
  rankdir?: 'TB' | 'BT' | 'LR' | 'RL'  // Direction
  align?: 'UL' | 'UR' | 'DL' | 'DR'     // Alignment
  nodesep?: number   // Horizontal spacing (default: 50)
  edgesep?: number   // Edge spacing (default: 10)
  ranksep?: number   // Vertical spacing (default: 50)
  ranker?: 'network-simplex' | 'tight-tree' | 'longest-path'
  marginx?: number   // Horizontal margin
  marginy?: number   // Vertical margin
}
```

## Direction Options

```
TB (default)          BT                   LR                   RL
Top → Bottom       Bottom → Top        Left → Right        Right → Left

    ┌─┐               ┌─┐                                           
    │A│              ┌┴─┴┐            ┌─┐   ┌─┐           ┌─┐   ┌─┐  
    └┬┘              │B│C│            │A│───│B│           │B│───│A│  
   ┌─┴─┐             └─┬┘             └─┘   └─┘           └─┘   └─┘  
   │B│C│               ▲                     │             │         
   └───┘             ┌─┴─┐                   ▼             ▼         
                     │ A │               ┌─┐   ┌─┐     ┌─┐   ┌─┐    
                     └───┘               │C│   │D│     │D│   │C│    
```

## Node Shapes

```
circle (default)    square          triangle         diamond
     ○               □                △                ◇
```

## Events

```vue
<DagreGraph
  :data="data"
  @node:click="(node, event) => handleNodeClick(node)"
  @node:mouseover="(node, event) => showTooltip(node)"
  @node:mouseout="(node, event) => hideTooltip()"
  @link:click="(link, event) => handleLinkClick(link)"
  @link:mouseover="(link, event) => highlightLink(link)"
  @link:mouseout="(link, event) => unhighlightLink()"
/>
```

## Common Patterns

### Dependency Graph with Status

```vue
<script setup>
const taskData = {
  nodes: [
    { id: 'task-1', label: 'Design', status: 'completed' },
    { id: 'task-2', label: 'Backend', status: 'in-progress' },
    { id: 'task-3', label: 'Frontend', status: 'pending' },
    { id: 'task-4', label: 'Testing', status: 'pending' },
  ],
  links: [
    { source: 'task-1', target: 'task-2' },
    { source: 'task-1', target: 'task-3' },
    { source: 'task-2', target: 'task-4' },
    { source: 'task-3', target: 'task-4' },
  ],
}

const statusColors = {
  completed: '#10b981',
  'in-progress': '#3b82f6',
  pending: '#9ca3af',
}

const getNodeColor = (node) => statusColors[node.status] || '#6b7280'

const getNodeShape = (node) => {
  if (node.status === 'completed') return 'square'
  if (node.status === 'in-progress') return 'circle'
  return 'triangle'
}
</script>

<template>
  <DagreGraph
    :data="taskData"
    :height="400"
    :nodeFillColor="getNodeColor"
    :nodeShape="getNodeShape"
    :linkArrows="'end'"
    :dagreSettings="{ rankdir: 'LR', nodesep: 80 }"
  />
</template>
```

### System Architecture

```vue
<script setup>
const systemData = {
  nodes: [
    { id: 'web', label: 'Web App', type: 'frontend' },
    { id: 'api', label: 'API Server', type: 'backend' },
    { id: 'db', label: 'Database', type: 'database' },
    { id: 'cache', label: 'Redis', type: 'infrastructure' },
  ],
  links: [
    { source: 'web', target: 'api' },
    { source: 'api', target: 'db' },
    { source: 'api', target: 'cache' },
  ],
}
</script>
```

## Gotchas

1. **Node IDs must be unique**: Each node needs a distinct `id` string
2. **Links reference node IDs**: `source` and `target` must match existing node IDs
3. **Avoid cycles**: Dagre works best with acyclic graphs
4. **Large graphs need space**: Increase height/width for many nodes
5. **nodeLabel vs label**: `nodeLabel` is the accessor prop, `label` is the data property

Overview

This skill builds DagreGraph components for Vue 3 to render hierarchical directed acyclic graphs with automatic layout. Use it to produce clear org charts, dependency trees, flowcharts, and decision trees with customizable node styles, layouts, and interactive events. It focuses on practical defaults while exposing layout and styling hooks for production diagrams.

How this skill works

The component accepts a simple data model of nodes and links and computes a Dagre hierarchical layout (TB, LR, BT, RL) to position nodes and route links. You supply accessors for labels, colors, shapes, and sizes; the component applies Dagre settings (rankdir, nodesep, ranksep, etc.) and renders SVG nodes and links, with optional arrows, legends, zoom, and drag interactions. Events such as node:click and link:mouseover let you integrate tooltips, navigation, or selection logic.

When to use it

  • Visualizing organizational structure or reporting lines
  • Mapping task dependencies and pipeline flows
  • Documenting system architecture and service relationships
  • Creating decision trees or process flowcharts
  • Displaying hierarchical data that benefits from automatic layout

Best practices

  • Ensure each node has a unique id and links reference those ids
  • Start with default spacing, then tune dagreSettings (nodesep, ranksep) for dense graphs
  • Use node level or status fields to drive colors and shapes for quick scanning
  • Disable zoom/drag in static exports or printable diagrams
  • Avoid cycles — Dagre assumes mostly acyclic graphs for stable layouts

Example use cases

  • Org chart: map executives, leads, and teams with level-based colors and click handlers
  • Dependency graph: show task progress with status-driven colors and shapes
  • System architecture: diagram services, databases, and caches with directional links
  • Flowchart/decision tree: use rankdir and spacing to align steps and branches visually
  • Interactive docs: attach node:mouseover to display tooltips or metadata panels

FAQ

What data format does the component expect?

Provide an object with nodes and links. Nodes need id and optional label/level. Links use source and target node ids.

How do I change layout direction and spacing?

Pass dagreSettings with rankdir (TB/LR/BT/RL) and adjust nodesep, ranksep, edgesep, marginx, marginy to control spacing.

Can I style nodes differently by status or level?

Yes. Supply functions for nodeFillColor, nodeStrokeColor, nodeShape, and nodeSize to return values based on node properties.