Custom Layout Algorithm: Built for Large Codebases
How we replaced Dagre with a custom Reingold-Tilford tree layout to handle large graphs for tighter spacing, better hierarchy, async layout for smooth imports.
Version 0.2.3 ships a new layout engine: a custom Reingold-Tilford tree layout with contour matching, designed specifically for directory trees. This replaces Dagre with an in-house implementation tuned for large, complex codebases.
Why Move Away from Dagre?
Dagre works well for small-to-medium trees. But as graphs grow past 1,000 nodes, two problems emerge:
- Wide, sparse layouts: Dagre spreads nodes horizontally to avoid overlap, creating sprawling diagrams that don't fit the viewport
- Sync blocking: Layout computation happens on the main thread, freezing UI during import
Our custom algorithm addresses both with a layered approach that packs nodes tighter and supports async computation.
What Changed
Custom Reingold-Tilford Algorithm
The new algorithm arranges nodes using the classic Reingold-Tilford tree layout with contour matching: parents centered over their children, with contour-based collision prevention. Result: graphs that are 30-40% more compact vertically and horizontally.
Async Layout
Large directory imports now use requestIdleCallback to compute layout without blocking the UI. A progress indicator shows import status.
Sync Fallback
Relayout operations (direction changes, beautify) still use synchronous layout for immediate feedback. The async path only applies to initial import.
Performance Impact
| Metric | Dagre | Custom |
| 1K nodes layout time | 800ms | 600ms |
| 5K nodes layout time | 4.2s | 1.8s |
| 10K nodes layout time | OOM | 3.5s |
| Average node spacing | 50px | 35px |
The custom algorithm handles 10K+ nodes where Dagre runs out of memory.
Migration Notes
If you have saved JSON exports from older versions, they still import correctly. The layout engine is chosen at runtime, not persisted.
Future Improvements
- Layer-by-layer progressive rendering during async layout
- Incremental relayout (only recompute affected subtree)
- Custom edge routing algorithms (orthogonal, spline)