TUI or GUI? TJ's Native Graphical Interface Experiment for Neovim

TJ DeVries builds a native GPU-rendered GUI for Neovim using GPUI and a C+Rust+TS+Lua hybrid stack.
Neovim core contributor TJ DeVries demonstrated an experimental project on a developer show: a native GPU-rendered GUI for Neovim built on Zed's GPUI framework, forming a C+Rust+TypeScript+Lua tech stack. Leveraging Neovim's architectural decoupling of editing logic and rendering, it delivers features impossible in a pure TUI — cross-file reference arrows, live bidirectional Markdown preview, a drag-to-edit colorscheme editor, and multi-instance AI chat sync via a Cursor SDK daemon. Hosts debated "practicality vs. GUI feel," rating it 6–7 out of 10. Built in under 10 days, the experiment points to a compelling hybrid architecture pattern: mature editing core + high-performance renderer + hot-reloadable script layer.
In a developer show centered on the theme "TUI or GUI," Neovim core contributor TJ DeVries demonstrated a surprisingly experimental project: wrapping Neovim in a native, GPU-rendered graphical interface. The project was "rated" live by Prime, Casey, Trash, and others, who debated whether it qualified as a terminal UI (TUI) or a genuine GUI. Setting aside the jokes and "brain rot simulator" banter, the project actually reveals a core architectural philosophy behind Neovim — and one possible shape of a modern editor's hybrid tech stack.
Why Neovim Is Built to Be Embedded
TJ repeatedly emphasized during the show that giving Neovim a GUI should be easy "these days." The reason lies in one of Neovim's original design goals: decoupling the act of drawing the interface from the editing logic.
In his words, Neovim can run in a mode where it no longer draws characters to a terminal itself. Instead, it tells an external host: "I know I'm embedded inside something else — I won't render anything. I'll just expose my state to you, you send me events, I process them, and I send back the updated state." This is a classic state- and event-driven external UI protocol.
He also mentioned a historical project, Firenvim, which lets you open Neovim directly inside webpage text fields. For example, if you're writing a long code-formatted reply on GitHub, Firenvim embeds Neovim into that text box, and writes the result back when you're done. Prime joked: "So you could get trapped inside a webpage text box and never escape." That quip neatly captures the boundary of Neovim's embedding capability — it hands off all rendering responsibility to an external GUI, while managing only the editing state itself.
This decoupling mechanism is implemented in Neovim via the msgpack-RPC protocol. When launched with --embed mode, Neovim doesn't attempt to control a terminal; instead, it communicates with an external host process via stdin/stdout. The host calls Neovim's API via RPC (e.g., nvim_input to send keystrokes, nvim_command to run commands), while Neovim notifies the host of UI changes through events (e.g., grid_line, grid_scroll). This protocol means any language capable of inter-process communication can become a Neovim frontend — which is the fundamental reason Neovide (Rust GUI), FVim (.NET GUI), and Firenvim (browser extension) can all exist. Compared to Vim's architecture of relying on terminal control sequences, this represents a fundamental design shift: Neovim's "interface" is, by design, a replaceable plugin at the architectural level — not a hardcoded implementation.
The Tech Stack: A Four-Language Mix of C + Rust + TypeScript + Lua

This GUI project is built on GPUI — the immediate-mode Rust graphics framework that the Zed editor open-sourced. The show identified a genuinely "stitched-together" tech stack:
- C: Neovim itself
- Rust: GUI rendering layer built on GPUI
- TypeScript/JavaScript: Plugin subsystem running via Bun
- Lua: Neovim's native configuration language
The room burst out laughing as Casey confirmed each layer one by one. But this mix wasn't arbitrary. TJ explained that the value of the Bun subsystem lies in hot reloading: he can change the behavior of GUI plugins (widgets) without recompiling the entire editor. In other words, the low-level system is built in Rust, while the overlay UI, dialogs, and interactive components can be rapidly iterated in JavaScript.
His reasoning was pragmatic: "If something is going to be done a thousand times and won't change anymore, we delete the JavaScript and rewrite it in Rust to make it faster." This is a classic "explore with a scripting language, then drop down to a systems language once stable" development path.
GPUI is the immediate-mode UI framework that the Zed team built in-house for their editor. Written in Rust, it calls graphics APIs directly — Metal on macOS, Vulkan/WebGPU elsewhere — for GPU-accelerated rendering, completely bypassing native system UI controls. Unlike traditional retained-mode frameworks (e.g., Qt, GTK), an immediate-mode framework re-describes the entire UI state every frame and lets the framework decide what needs to be redrawn — conceptually similar to React's virtual DOM, but happening at the GPU rendering layer. GPUI's strengths are extremely low latency and zero framework-level constraints on custom rendering. The tradeoff is that developers must manage more rendering details manually. After Zed open-sourced in 2024, GPUI was published as a standalone crate, enabling other projects (like the Neovim GUI experiment described here) to reuse this rendering infrastructure without building a GPU pipeline from scratch.
What a GUI Can Do That a TUI Cannot

The show demonstrated a series of features that would be difficult or impossible to implement in a pure TUI. The most celebrated was reference arrows — when viewing references to a symbol, the interface draws arrows directly pointing to the locations in the code where it's used, spanning an entire large file. Casey had specifically requested this feature, and TJ put it bluntly: "A TUI could never." The reason is straightforward: a terminal can only draw one thing per character cell in a grid. It can't layer visuals, and it can't overlay graphics on top of text.
Other demonstrated GUI features included:
- Live Markdown preview: A floating window renders images, checklists, code highlighting, sequence diagrams, and tables, with real-time bidirectional sync to the source buffer. TJ quipped that "reading a table in Vim is basically impossible," and LLMs love outputting tables and sequence diagrams.
- Native right-click menu: A place for operations you use once a month and aren't worth memorizing shortcuts for.
- Real-time colorscheme editor: Drag to change, zero latency. TJ took a shot at VS Code: "VS Code can't do this."
- Interactive Cursor chat integration: A background daemon running the Cursor SDK, syncing conversation state across multiple Neovim instances and browser views.
On the rendering pipeline, TJ gave a clear explanation: Neovim has callbacks on byte changes, which trigger Tree-sitter to re-parse, whose results are passed to the GUI layer, which GPUI then renders. The whole pipeline is built around speed — "If I'm going to vibe code it, it at least needs to be fast."
Tree-sitter is an incremental syntax parsing library, originally developed by GitHub and later deeply integrated into Neovim. Its core advantage is the "incremental" part: when file content changes, Tree-sitter doesn't re-parse the entire file — it only updates the affected syntax tree nodes. This means that even for files with tens of thousands of lines, re-parsing after each keystroke completes in milliseconds. In the rendering pipeline described above, Tree-sitter's parse results (the concrete syntax tree) are the data foundation for features like highlighting, folding, and reference jumping. When the GUI layer draws reference arrows, it needs precise symbol position information from Tree-sitter — not regex matching. This is also why the architecture can fire a callback on byte changes and quickly refresh rendering: Tree-sitter's incremental nature keeps the latency of the "edit → parse → render" chain low enough that it never becomes a performance bottleneck.
The Central Debate: What Actually Counts as a GUI?

The most interesting part of the show was the disagreement among hosts over the "standard" for a GUI. TJ consistently argued from the perspective of functional usefulness, while Casey explicitly pushed back against using "practicality" as the measure: "The point of a GUI isn't that it's useful — it's that it does weird things that prove it's a GUI."
Behind what sounds like a joke is a genuine product philosophy debate. The reviewers pointed out several shortcomings of the project:
- Many features are just "pop-up modal windows" floating over the editor, lacking the integrated feel of being truly embedded;
- No animated transitions — several hosts (half-jokingly) argued that animation is the hallmark of a GUI;
- The Cursor chat window "looks like it just plopped a terminal on screen," without using graphical capabilities to add rounded corners, arrows, or other details that a TUI couldn't achieve.
Prime even sarcastically listed a "GUI must-have": visible input latency. "It's too fast — it makes me feel like I'm using a TUI. You need to add 100 milliseconds of delay to every keystroke." That jab points to a real observation: many so-called GUI editors (like VS Code) pay for their graphical richness precisely with sluggishness and latency.
Ultimately the project was rated "6 to 7 out of 10" (0 being pure TUI, 10 being pure GUI). The consensus: it "crosses the threshold — it's definitely a GUI" — but there's further to go. TJ himself admitted the project was built in "under 10 days."
An Industry Signal the Show Left Unspoken
When Trash asked "do any other editors have these capabilities?", TJ was candid: "Emacs probably has every single one of these features. I'm only scratching the surface of what Emacs users have already done — except they're doing it at 0.001 frames per second." That joke points to a long-standing tradeoff: extensibility and performance are rarely easy to have simultaneously.
The real significance of this experiment may not be "should Neovim have a GUI" — but rather that it demonstrates a hybrid architecture paradigm: combining a mature editing core (Neovim) + a high-performance rendering framework (GPUI/Rust) + a hot-reloadable scripting layer (Bun/TS), layering graphical capabilities on top without touching the editing logic. For developers tracking the evolution of editor technology and AI-assisted programming (the show featured heavy Cursor SDK integration), this is a direction worth watching.
Related articles

Python 3.15 New Feature: Is the sentinel Type Better Than None?
Python 3.15 adds the sentinel type, using named unique sentinel objects to replace None and bare object(), resolving confusion among missing values, explicit emptiness, and invalid input while avoiding type hint failures.

Deep Learning's Three Architectures and the Universal Approximation Theorem: A Complete Guide to MLPs, CNNs, and Transformers
Explaining the inductive bias differences among MLP, CNN, and Transformer, the existence meaning of the Universal Approximation Theorem (UAT), and the role of Taylor expansion in approximation analysis, while clearing up common misconceptions.

GitLab Emergency Patch Release: Analysis of Critical Security Fixes
GitLab releases critical patch 19.5.1, sparking 355 points of discussion on Hacker News. This article analyzes the background, upgrade steps for self-hosted instances, and verification points to help DevOps teams respond quickly to security updates.