API Reference
Types, functions, and the React component for @joycostudio/trazo.
Entry points
import {
layoutFlow, layoutGit, layoutSequence, layoutBlock, layout,
flow, git, seq, block, // tagged-template parsers (throw on error)
} from "@joycostudio/trazo";
import { Graph } from "@joycostudio/trazo/react"; // React is a peer dependencyLayout functions
layoutFlow(input, options?)
Lay out a flowchart graph and return a positioned result ready for rendering.
layoutFlow(input: FlowGraph, options?: FlowLayoutOptions): PositionedGraphlayoutGit(input, options?)
Lay out a commit DAG.
layoutGit(input: CommitGraph, options?: LayoutOptions): PositionedGraphlayoutSequence(input, options?)
Lay out a sequence diagram (participants + ordered messages + notes).
layoutSequence(input: SequenceGraph, options?: SequenceLayoutOptions): PositionedGraphlayoutBlock(input, options?)
Lay out a block-grid wireframe (Mermaid block-beta).
layoutBlock(input: BlockGraph, options?: BlockLayoutOptions): PositionedGraphlayout(input, options?)
Convenience dispatcher — routes by input.kind: "flow" → layoutFlow, "sequence" → layoutSequence, "block" → layoutBlock, otherwise layoutGit.
layout(input: CommitGraph | FlowGraph | SequenceGraph | BlockGraph, options?): PositionedGraphTagged templates
flow, git, seq, and block parse DSL source inline and throw on a syntax error (vs parseFlow/parseGit/parseSequence/parseBlock, which return { graph, error }):
import { seq, layoutSequence } from "@joycostudio/trazo";
const positioned = layoutSequence(seq`
participant Client
participant Server
Client ->> Server : request
Server -->> Client : response
`);Input types
CommitGraph
interface Commit {
id: CommitId; // unique string
parents: CommitId[]; // mainline first; [] = root; 2+ = merge
branch?: string;
message?: string; // drives label sizing
author?: string;
hash?: string; // short hash shown mono before the subject
}
interface CommitGraph {
commits: Commit[];
refs?: Record<string, CommitId>; // e.g. { main: "a1" }
}FlowGraph
type NodeShape = "dot" | "box" | "stadium" | "diamond" | "cylinder";
type SemanticRole = "primary" | "success" | "error" | "warning" | "streamed" | "neutral";
type FlowDirection = "TD" | "LR";
interface FlowNode {
id: NodeId;
label?: string; // \n / <br> → multi-line
shape?: NodeShape; // default "box"
role?: SemanticRole; // controls color
group?: NodeId; // subgraph membership (see FlowGraph.groups)
}
interface FlowEdge {
from: NodeId;
to: NodeId;
label?: string;
colored?: boolean; // true → inherits source node's role color
arrow?: "none" | "end" | "both"; // default "end" (directed); "none" = undirected
}
interface FlowGraph {
kind: "flow";
nodes: FlowNode[];
edges: FlowEdge[];
direction?: FlowDirection;
groups?: { id: NodeId; label?: string }[]; // subgraph containers
}SequenceGraph
type MessageKind = "sync" | "async" | "self";
interface SequenceParticipant { id: NodeId; label?: string; role?: SemanticRole; }
interface SequenceMessage { from: NodeId; to: NodeId; label?: string; kind: MessageKind; seq?: number; }
interface SequenceNote { over: NodeId[]; text: string; seq?: number; }
// `seq` is the global event order across messages AND notes (0-based). The
// parser stamps it from source line order so notes interleave on the timeline;
// omit it when building a graph by hand and notes sort after all messages.
interface SequenceGraph {
kind: "sequence";
participants: SequenceParticipant[];
messages: SequenceMessage[];
notes?: SequenceNote[];
}BlockGraph
interface BlockCell {
id: NodeId;
label?: string;
shape?: NodeShape;
role?: SemanticRole;
span?: number; // column span, default 1
}
interface BlockGraph {
kind: "block";
columns: number;
cells: BlockCell[];
}Output: PositionedGraph
Both layout functions return a PositionedGraph. Pass it directly to <Graph /> — you rarely need to inspect the fields.
interface PositionedGraph {
nodes: PositionedNode[];
edges: PositionedEdge[];
width: number; // total diagram width in px (use for container/viewBox sizing)
height: number; // total diagram height in px
laneCount: number;
groups?: PositionedGroup[]; // flow subgraph boxes / sequence note boxes
lifelines?: Lifeline[]; // sequence-diagram lifelines
}
interface PositionedGroup {
id: NodeId; label?: string;
x: number; y: number; w: number; h: number; // top-left + size (NOT center)
variant?: "group" | "note";
}
interface Lifeline { id: NodeId; x1: number; y1: number; x2: number; y2: number; }width and height include padding and are ready for viewBox="0 0 {width} {height}".
Layout options
Git: LayoutOptions
interface LayoutOptions {
laneWidth?: number; // default 28 px
rowHeight?: number; // default 40 px (minimum spacing in horizontal mode)
padding?: number; // default 16 px
edgeStyle?: "elbow45" | "orthogonal"; // default "elbow45"
orientation?: "vertical" | "horizontal"; // default "vertical"
labelSide?: "left" | "right"; // default "right"
}In horizontal mode, rowHeight is the minimum inter-commit spacing — commits automatically spread further apart when adjacent label badges would otherwise overlap.
Flow: FlowLayoutOptions
interface FlowLayoutOptions {
direction?: "TD" | "LR"; // default "TD"
layerGap?: number; // default 56 px — gap between layers
nodeGap?: number; // default 28 px — gap between sibling nodes
padding?: number; // default 24 px — outer padding
minNodeWidth?: number; // default 64 px
nodeHeight?: number; // default 36 px
labelPadX?: number; // default 16 px — horizontal padding inside labels
edgeStyle?: "elbow45" | "orthogonal"; // default "elbow45"
}Sequence: SequenceLayoutOptions
interface SequenceLayoutOptions {
columnGap?: number; // default 120 px — between lifeline columns
rowGap?: number; // default 48 px — between message rows
headerHeight?: number;// default 40 px — participant header box height
padding?: number; // default 24 px
edgeStyle?: "elbow45" | "orthogonal"; // default "orthogonal"
}Block: BlockLayoutOptions
interface BlockLayoutOptions {
cellGap?: number; // default 12 px
rowHeight?: number; // default 48 px
minCellWidth?: number;// default 96 px
padding?: number; // default 24 px
}<Graph /> component
import { Graph } from "@joycostudio/trazo/react";
<Graph
graph={positionedGraph}
title="Flowchart" // sets <title> and aria-label on the <svg>
/>- Pure function of props — no hooks, no effects, no browser globals.
- Renders identically as a React Server Component or when client-hydrated.
- Wraps output in
<svg data-slot="trazo-graph">.
Prefer styling via data-slot from CSS (**:data-[slot=group]:…). Inner slots: edge, edge-label, node, label, group (subgraph box), note (sequence note box), group-label, lifelines / lifeline. A narrow type-safe classNames map is also accepted:
interface GraphClassNames {
edge?: string; node?: string; label?: string; nodeBox?: string; edgeLabel?: string;
group?: string; groupLabel?: string; lifeline?: string;
}Theming
Every color the engine emits is a token key (never a literal), resolved through a three-layer CSS-variable chain:
var(--trazo-<slot>, var(--color-<shadcn-token>, var(--<shadcn-token>, <hex>)))--trazo-<slot>— trazo's own override knob. Unset by default, so it falls through. Set these to re-theme the graph.--color-<shadcn-token>/--<shadcn-token>— the stock shadcn token the slot adopts by default, so an unthemed graph already matches the consuming app's brand. (In the JOYCO UI kit--primaryis the brand blue, so JOYCO apps stay on-brand with zero config.)<hex>— a last-resort literal, so the SVG is never colorless in an app with no shadcn tokens at all.
To theme the graph, set the --trazo-* variables on any ancestor element. You don't need to know which shadcn token a slot maps to — the slot names are trazo's stable vocabulary.
/* Override knob (default) → shadcn token it adopts */
/* Flow node roles */
--trazo-primary: /* → --primary */
--trazo-success: /* → --chart-2 */
--trazo-error: /* → --destructive */
--trazo-warning: /* → --chart-4 */
--trazo-streamed: /* → --chart-3 */
--trazo-neutral: /* → --muted-foreground */
/* Label text on each role box — a -foreground variant of each */
--trazo-primary-foreground: /* … --success-foreground, --error-foreground, … */
/* Git lanes — 6 distinct slots, auto-cycling (mod 6) for extra branches */
--trazo-lane-1: /* → --primary */
--trazo-lane-2: /* → --chart-1 */
--trazo-lane-3: /* → --chart-2 */
--trazo-lane-4: /* → --chart-3 */
--trazo-lane-5: /* → --chart-4 */
--trazo-lane-6: /* → --chart-5 */
/* Label text on each lane chip */
--trazo-lane-1-foreground: /* … --trazo-lane-6-foreground */
/* Page text + strokes (git labels, neutral edges) */
--trazo-foreground: /* → --foreground */
/* Git label badge accent surface + its text */
--trazo-accent: /* → --accent */
--trazo-accent-foreground: /* → --accent-foreground */
/* Subgraph container outline + sequence lifelines (a muted gray) */
--trazo-muted-foreground: /* → --muted-foreground */
/* Sequence note box fill */
--trazo-muted: /* → --muted */
/* Node "cutout" background — must match the surface behind the SVG */
--trazo-bg: /* → --background */Brand-matched by default. Because each slot defaults to a stock shadcn token, dropping trazo into any Tailwind/shadcn app recolors the diagram to that app's palette with no setup. Set the --trazo-* slots only when you want to override that default.
Arrowheads follow their edge. A directed edge's arrowhead is filled with the same token as the edge's stroke, so the --trazo-* role/lane/accent slots recolor heads automatically — there's no separate arrowhead variable.
Surfaces. Subgraph boxes and sequence lifelines use --trazo-muted-foreground for their outline; sequence note boxes fill with --trazo-muted. The git label badge uses --trazo-accent / --trazo-accent-foreground. Override those to restyle the new primitives.
Example — dark branded theme:
// Wrap the <Graph /> in a div that sets the override slots
<div
style={{
background: "#0f0f14",
"--trazo-bg": "#0f0f14",
"--trazo-primary": "#3b82f6",
"--trazo-success": "#22c55e",
"--trazo-error": "#ef4444",
"--trazo-warning": "#f59e0b",
"--trazo-streamed": "#a855f7",
"--trazo-lane-1": "#3b82f6",
"--trazo-lane-2": "#22c55e",
"--trazo-lane-3": "#f59e0b",
"--trazo-accent": "#3b82f6",
} as React.CSSProperties}
>
<Graph graph={graph} title="My diagram" />
</div>You can also theme via a class instead of inline styles — e.g. className="[--trazo-success:#16a34a]", or a named CSS rule (.trazo-ocean { --trazo-primary: #0369a1; }) on any ancestor.
--trazo-bg is the one slot that must be set explicitly when you change the background — commit circles use it to punch a gap around the center dot so it reads correctly against a colored lane line. (It defaults to --background, so it only needs an explicit value when the graph sits on a non---background surface like a --card/--muted panel.)