Post

OpenTUI vs Spectre.Console: Same Dashboard, Two Stacks

OpenTUI vs Spectre.Console: Same Dashboard, Two Stacks

Built the same live market data dashboard twice — once in OpenTUI (TypeScript/Bun) and once in Spectre.Console (C#/.NET 10) — both pulling real data from Binance public APIs.

Source: karlobrien/tui-work

What it does

Four-area UI: header with WebSocket connection status, a live ticker table streaming prices for BTCUSDT / ETHUSDT / SOLUSDT / BNBUSDT via Binance miniTicker WebSocket, an instrument detail panel refreshed via REST on symbol change, and a footer with keyboard hints. Tab/Shift+Tab to switch symbols, q to quit.

OpenTUI (TypeScript/Bun)

OpenTUI uses a retained-mode scene graph — you create Box and TextRenderable nodes, mutate their properties, and the renderer diffs and redraws at a target FPS (set to 10). Layout is CSS Flexbox via Yoga.

The main friction point: the API is low-level. There’s no table primitive, so a “table” is just rows of Box nodes with manual padding. The event system (keyInput) requires a specific event name that isn’t obvious from the types. Mutable TextRenderable instances for live values work well once you understand the pattern — updating .content or .fg in place triggers a repaint without rebuilding the tree.

Auto-reconnect on the WebSocket was straightforward to implement because Bun’s native WebSocket API is clean.

Spectre.Console (C#/.NET 10)

Spectre uses a push-to-render model via AnsiConsole.Live(). You call ctx.UpdateTarget() with a freshly-built renderable tree — the library diffs the ANSI output and redraws only changed regions. Tables, Panels, and Columns are first-class; markup like [green]+1.2%[/] handles coloring inline.

The model is simpler to reason about: rebuild the full view on every tick, let the library handle diffing. The tradeoff is that you’re allocating a new object graph on every WebSocket message, though at 10 ticks/sec for four symbols this is negligible.

C# async here is clean — Task.WhenAny for the key-read loop vs. the WebSocket task, structured cancellation via CancellationTokenSource.

Verdict

Spectre.Console wins on productivity. Rich built-in primitives, inline markup, and a dead-simple rendering model mean less boilerplate. It took roughly half the code to get the same result.

OpenTUI is more capable for complex interactive UIs — retained scene graph + Flexbox layout gives you fine-grained control — but the current API surface is rough. Docs are sparse and the types don’t always reflect actual behaviour.

If I were building a long-lived terminal tool I’d reach for Spectre in .NET or look harder at a more mature TypeScript TUI (Ink, Blessed) before committing to OpenTUI.

This post is licensed under CC BY 4.0 by the author.