Writing TUI in the Terminal with Angular: Introducing @cyia/opentui-angular
Foreword
Terminal applications (TUI, Text User Interface) have always had a unique charm - lightweight, fast, and cross-platform. From htop to vim, from the interactive wizard of npm init to database management tools, excellent terminal interfaces can make command-line workflows more efficient.
However, developing a terminal application usually requires dealing directly with low-level rendering details: calculating character buffers, handling ANSI escape sequences, listening for terminal resize events... These tedious tasks often overshadow the business logic itself.
Recently, an Angular adapter for the OpenTUI project has emerged - @cyia/opentui-angular. It allows developers familiar with Angular to build terminal interfaces using the patterns they already know.
OpenTUI itself provides a comprehensive component system (text, box, input, scrollbox, code, diff, etc.) and a set of hooks APIs (keyboard events, focus management, paste handling, timeline animations, etc.), but its design is deeply coupled to React - it relies on react-server-dom-flight to drive the rendering loop.
The Positioning of the Angular Adapter
What @cyia/opentui-angular does is clear: retain the rendering capabilities of OpenTUI core, while replacing the React rendering engine with Angular's component system. Specifically, this adapter accomplishes the following:
1. Custom Platform Layer
Angular's Ι΅internalCreateApplication accepts a platformProviders parameter, allowing developers full control over the underlying DOM adaptation. The OpenTUI adapter implements a TDomAdapter - since terminal applications don't need a real browser DOM, this adapter provides no-op implementations or stub functions for most DOM APIs, and takes over RendererFactory2 and Sanitizer, bridging them to OpenTUI's renderer.
2. Component Mapping
OpenTUI core exports a large number of Renderable classes (such as BoxRenderable, TextRenderable, InputRenderable, etc.), which are essentially the "HTML tags" of the terminal world. The adapter registers these Renderables as Angular custom elements, and with the NO_ERRORS_SCHEMA configuration, developers can directly use tags like <box>, <text>, <input> in their templates:
@Component({
selector: 'app-root',
schemas: [NO_ERRORS_SCHEMA],
template: `
<box>
<text [content]="message()" fg="cyan" />
</box>
`,
})
export class App {
protected message = signal('Hello from Angular + OpenTUI');
}
3. Angular-ifying Hooks
OpenTUI's native hooks (like useRenderer, useKeyboard) depend on React's context mechanism. The adapter rewrites these hooks to use Angular's inject(), effect(), and computed(), enabling them to function correctly within Angular's dependency injection system:
useKeyboard((key) => {
if (key.name === 'escape') {
process.exit(0);
}
});
4. Slot System Adaptation
OpenTUI's plugin system allows external components to be inserted into the render tree through a SlotRegistry mechanism. The adapter implements createAngularSlotRegistry, bridging Angular's TemplateRef, NgTemplateOutlet, and ComponentRef with OpenTUI's slot protocol, so that Angular templates can be rendered into the terminal interface as plugin content.
5. Keymap Submodule
The adapter also publishes a separate subpath export @cyia/opentui-angular/keymap, providing an Angular Signal-based key mapping solution. With utility functions like useKeymap, useBindings, and useActiveKeys, developers can define keyboard shortcut bindings declaratively, supporting advanced interaction patterns like leader keys and command completion.
6. Component Extension Capability
OpenTUI core provides an extend() API to allow custom Renderables. The adapter retains this capability - you can inherit from existing Renderables (e.g., BoxRenderable), override the rendering logic, and then register them as new tags for use:
class ConsoleButtonRenderable extends BoxRenderable {
protected override renderSelf(buffer: OptimizedBuffer): void {
super.renderSelf(buffer);
// Custom button text drawing logic
}
}
extend({ consoleButton: ConsoleButtonRenderable });
Included Examples
The project itself comes with a fairly rich set of examples covering common TUI scenarios:
| Example | Description |
|---|---|
| Basic Demo | Login form, focus management, styled text (bold/italic/colors) |
| Counter Demo | Timed re-rendering driven by Signal state updates |
| Animation Demo | System monitoring animation driven by the Timeline API |
| Borders / Box | Border styles and layout components |
| Scrollbox | Scrollable content area |
| Code / Line Number | Code highlighting and line number display |
| Diff | Text diff comparison view |
| Hooks Demo | Comprehensive demonstration of various hooks |
| Keymap Demo | Complete keyboard shortcut system, supports leader keys, command completion |
| Extend Demo | How to extend custom Renderables |
| Flush Sync | Synchronous flush rendering |
Tech Stack
- Angular 22 - The latest Angular version, fully adopting Signals and the new control flow syntax
- OpenTUI core ^0.4.x - The underlying TUI rendering engine
Usage
Quick Start
The project provides a GitHub template that can be cloned with a single command:
git clone https://github.com/wszgrcy/ngx-opentui-starter.git
cd ngx-opentui-starter
npm install && npm start
Manual Integration
If your project is already based on Angular, after installing the dependencies you only need a small amount of configuration at the application entry point:
import { bootstrapApplication, CliRendererToken } from '@cyia/opentui-angular';
import { createCliRenderer } from '@opentui/core';
const renderer = createCliRenderer();
renderer.then((cliRenderer) =>
bootstrapApplication(App, {
providers: [{ provide: CliRendererToken, useValue: cliRenderer }],
}),
);
Then remember to add schemas: [NO_ERRORS_SCHEMA] in your component - this is a necessary configuration for using custom elements like <text> and <box>.
Who Is It For?
- Angular Developers - If you want to build terminal applications but don't want to learn React or a completely new framework, this adapter lets you directly apply your existing Angular knowledge.
- Node.js Projects Needing a TUI - Scenarios like interactive CLI tools, operations dashboards, database management interfaces, etc.
- OpenTUI Users - If you are already using OpenTUI but are more accustomed to Angular's development patterns, this adapter provides a migration path.
Summary
What @cyia/opentui-angular does is not complicated - it is essentially a bridging layer that exposes the rendering capabilities of OpenTUI core as Angular components and hooks. But it solves a practical problem: allowing Angular developers to build terminal interfaces in the most natural way.
If you are looking for a solution that enables an Angular project to quickly gain TUI capabilities, or if you are simply curious about the idea of "using a virtual DOM in the terminal," this project is worth exploring.
- npm:
@cyia/opentui-angular - Source Code: GitHub
- Quick Start Template: ngx-opentui-starter
Comments
No comments yet. Start the discussion.