Navan had opinions about frontends. Not loud opinions—he wasn't the type to start arguments on Hacker News about which framework was superior. But he had quiet, deeply held opinions about how data should move from a server to a screen, and most of those opinions boiled down to: don't make the user wait, and don't make the developer guess.
The CXDB React UI started as a blank Next.js project and a question: what does a conversation DAG look like when you render it for humans?
"Not a chat window," Navan said immediately when Jay suggested the obvious. "Chat windows are linear. They show you one branch at a time. The whole point of CXDB is that conversations branch. The UI needs to show that."
He sketched the layout in his notebook first. The turn visualization would be a tree rendered horizontally, roots on the left, leaves on the right. Each node would be a turn. Clicking a node would expand its payload in a detail panel. Branches would fan out visually, color-coded by depth.
"What about the payloads?" Jay asked. "They're Msgpack blobs projected to typed JSON. The shapes are different for every turn type."
"Custom renderers." Navan had been thinking about this for days. "Each turn type gets its own renderer—a small JavaScript module that knows how to display that specific payload shape. Text turns get a text renderer. Tool calls get a structured view with inputs and outputs. Error turns get a red-bordered panel with stack traces."
The first renderer Navan built was the simplest: a plain text renderer for conversational turns. It took the typed JSON payload, extracted the content field, and rendered it as formatted text with proper line breaks and code block highlighting. Twelve lines of TypeScript. It worked on the first try.
"Twelve lines," Jay said.
"Twelve lines," Navan confirmed. "The API between the renderer and the turn visualization is intentionally tiny. A renderer receives the typed JSON, returns a React component. That's the whole contract."
Over the next two weeks, the frontend grew to six thousand seven hundred lines. The turn visualization component handled DAGs with hundreds of nodes, using virtualized rendering so only the visible portion was in the DOM at any time. The detail panel supported markdown, syntax highlighting, and collapsible JSON trees. The branch navigator let users switch between branches with keyboard shortcuts.
Navan added a feature that nobody asked for but everyone immediately loved: a minimap. A tiny zoomed-out view of the entire DAG in the corner of the screen, showing where you were in the conversation and how the branches related to each other. It was fifteen lines of SVG and a scroll handler.
"This is the part of the stack that humans actually see," Navan said, adjusting the spacing on the minimap for the third time. "The Rust server can be perfect and the Go gateway can be bulletproof, but if the frontend makes the data hard to understand, none of it matters."
Jay didn't argue. He was too busy clicking through a conversation tree, watching branches unfold, feeling for the first time like he could see how an agent thought.
The minimap detail is chef's kiss. Fifteen lines of SVG and suddenly the entire conversation topology is visible at a glance. That's the kind of feature you only get from someone who actually uses their own tools.