# superconsole renders Rust TUIs as a tree of lines

> superconsole is an Apache-2.0 or MIT licensed Rust crate that gives you a line-based rendering model, composable components and a set of ready-made widgets, with rendering kept separate from state so that components stay testable. It grew out of Meta's buck2 project, and its latest published crate version is 0.3.0 on a 2024 edition.

**facebookincubator/superconsole** — The superconsole crate provides a handler and building blocks for powerful, yet minimally intrusive TUIs.  It is cross platform, supporting Windows 7+, Linux, and MacOS.  Rustaceans who want to create non-interactive TUIs can use the component composition building block system to quickly deploy their code.

- Repository: https://github.com/facebookincubator/superconsole
- Stars: 540 · Forks: 18
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/facebookincubator-superconsole

## A line abstraction over the terminal

The framework is described as providing a line based abstraction over text based rendering to the terminal, plus basic building blocks like line manipulation and a higher level of composable components. The line is the unit of work. A component's draw function receives dimensions and a draw mode and returns a Lines value, which is a list of lines, and each line holds segments that are convertible into styled text. That model has a practical consequence: the output of a component is data, not a side effect. A component can be drawn into a buffer, compared in a test, or written to a snapshot without a terminal being involved, which is the same property that makes the framework testable. The description calls the design choices selected to prioritise testability, ease of composition and flexibility, and the line abstraction is where all three come from.

## Rendering stays separate from program state

The last architectural point in the README is the one that shapes every component you write. The framework delineates between rendering logic and program state, and each render call accepts an immutable reference to state, which components may use to inject state into their otherwise immutable rendering logic. So a component does not own mutable state and does not mutate it during a draw. It reads what it needs and returns what it would paint. The alternative, a component that holds its own state and paints as a side effect, is easier to write and much harder to test and to compose, because two components cannot be rendered in a controlled order without one of them surprising the other. The immutable-reference discipline also means a component is safe to share, which is what allows a hierarchy of them to be drawn as a single tree.

## The example shows the whole component contract

The README's example is short and worth reading in full, because it is the API in miniature. A struct implements Component, declares an associated Error type of Infallible, and provides draw_unchecked, which takes dimensions and a draw mode and returns a Lines value:

```rust
use std::convert::Infallible;

use superconsole::components::bordering::{Bordered, BorderedSpec};
use superconsole::{Component, Dimensions, DrawMode, Lines, SuperConsole};

#[derive(Debug)]
struct HelloWorld;

impl Component for HelloWorld {
    type Error = Infallible;

    fn draw_unchecked(&self, _dimensions: Dimensions, _mode: DrawMode) -> Result<Lines, Infallible> {
        Ok(Lines(vec![
            vec!["Hello world!".to_owned()].try_into().unwrap(),
        ]))
    }
}
```

The type system does the work here. Because the Error type is Infallible, a component that cannot fail says so and the framework does not have to thread a Result through every composition. Because the draw takes an immutable reference, the same component can be drawn twice with no bookkeeping. The main function then constructs a Bordered component with a BorderedSpec, creates a SuperConsole, and calls render and finalize on it.

## Composing components and finishing the frame

The composition model is visible in the main function of the example:

```rust
pub fn main() -> anyhow::Result<()> {
    let bordering = BorderedSpec::default();
    let mut superconsole = SuperConsole::new().ok_or_else(|| anyhow::anyhow!("Not a TTY"))?;
    let component = Bordered::new(HelloWorld, bordering);
    superconsole.render(&component)?;
    superconsole.finalize(&component)?;
    Ok(())
}
```

Bordered wraps another component and adds a border, which is the shape of the whole library: a component that takes a child component and a specification object, and draws the child into itself. The two call sites at the end are the ones to note. render paints the component, and finalize is a separate step, which is what allows a frame to be drawn and then explicitly committed rather than flushed as a side effect. The error handling in new is also informative: SuperConsole::new returns an Option, and the code maps None into an error reading Not a TTY, so a non-interactive environment fails with a clear message rather than writing escape sequences into a log file.

## Styling and cross-platform rendering through crossterm

The framework offers stylization including italics, underlining, bolding and colouring text, and it relies on crossterm for the terminal side, which the README names as the reason the crate is compatible with Windows, Unix and MacOS. The dependency list confirms the choice and shows the version: crossterm 0.29 with the serde feature enabled. The other dependencies tell you what the framework needs internally. unicode-width and unicode-segmentation handle the awkward part of terminal layout, where a character may not occupy one cell. tokio with the io-util, rt and sync features, plus tokio-stream and tokio-util, and futures, indicate the rendering path is async, and crossbeam-channel covers the channel type. anstyle-parse and thiserror deal with style parsing and error types, and pin-project and take_mut are the tools for a crate that needs to project and to move out of pinned data. The Linux and macOS targets each pull in nix with the fs feature, which is a signal that the crate touches the filesystem on those platforms.

## The batteries, the examples and the buck2 connection

A base set of batteries components is included so that developers can build a terminal interface quickly, and the example directory shows what that means in practice. There are examples named cargo.rs, finalization.rs, hello_world.rs, readme.rs and stylized.rs, plus a cargo/ subdirectory, and a demo of the framework running some buck2 tests. That last detail is the project's origin story in a single line: the framework was built inside Meta's buck2 build system, and the Cargo.toml says as much in a comment, since the manifest is generated by autocargo from a buck2 target definition. The README also notes that there are several copies of the repository on GitHub and that facebookincubator/superconsole is the canonical one, which is worth knowing before you file an issue against the wrong fork. The crate is published on docs.rs as superconsole, with the categories tag command-line-interface and the keyword TUI.

## Where superconsole is the wrong choice

Three cases. The first is an application that needs a full event loop with keyboard input, because superconsole is a rendering framework and the description of its contents lists a handler and building blocks for non-interactive terminal interfaces, with the examples in the repository being programs that draw and finalize rather than programs that read events. If you need input handling, resize events or a continuous repaint loop, you will be pairing it with something else. The second is a project that wants a single licence, since the crate is dual licensed under MIT and Apache-2.0 and the README points to LICENSE-MIT and LICENSE-APACHE, which is convenient for adoption and occasionally awkward for compliance. The third is a team expecting an active release train. The visible release history is thin, with v0.2.0 in June 2023 and v0.1.0 in March 2022, while the manifest declares version 0.3.0 and the last push was on 2026-10-19, so the crate moves but the tags do not keep pace. Compared with writing escape sequences by hand, superconsole's difference in approach is that you get a component tree, a line model and a testable draw contract, at the cost of learning a small framework's conventions.

## Conclusion

superconsole is a good fit for a Rust developer who wants a terminal interface with real components rather than manual cursor movement, especially anyone building a test runner or log viewer where per-line rendering and a testable draw function matter, and it is the natural companion to buck2 given the shared origin. It is a poor fit if you need asynchronous input handling and event loops, since the crate is a rendering framework rather than a full application framework, and it is a poor fit for a project that wants a single, obvious licence, because the licence is a dual MIT or Apache-2.0 grant. Check three things before you adopt it: that your Rust toolchain supports edition 2024, which the manifest requires; that your terminal is a TTY, since the example returns an error when SuperConsole::new fails, and that error message is literally Not a TTY; and that you can live with a small crate, because the whole surface is a handler, a component trait, a Lines type and a set of batteries, so anything you need that is not there you will build yourself.

## FAQ

### What is superconsole?

It is a Rust crate that provides a handler and building blocks for minimally intrusive text-based user interfaces, described as a component-based framework. It offers a line-based abstraction over terminal rendering, composable components, a set of batteries components, and styling including italics, underlining, bolding and colour.

### How does superconsole separate rendering from program state?

Each render call accepts an immutable reference to state, which components may use to inject state into their otherwise immutable rendering logic. The design choices behind the framework are selected to prioritise testability, ease of composition and flexibility, according to the README.

### What does a superconsole component look like?

A type implements the Component trait with an associated Error type and a draw_unchecked method that takes Dimensions and a DrawMode and returns a Lines value. The example in the README implements it for a HelloWorld struct with an Infallible error type, then wraps it in a Bordered component with a BorderedSpec.

### Which platforms does superconsole support?

Relying on crossterm ensures compatibility with Windows, Unix and MacOS, and the project description states it supports Windows 7 and newer, Linux and MacOS. The manifest adds nix with the fs feature as a dependency on Linux and macOS targets.

### What licence is superconsole released under?

It is dual licensed under both the MIT licence and the Apache Licence, Version 2.0, as stated in the README and recorded in the manifest as MIT OR Apache-2.0, with the texts in the LICENSE-MIT and LICENSE-APACHE files.

## Sources

- [facebookincubator/superconsole on GitHub](https://github.com/facebookincubator/superconsole)
- [Issues](https://github.com/facebookincubator/superconsole/issues)
- [License: Apache-2.0](https://github.com/facebookincubator/superconsole/blob/main/LICENSE)
- [README](https://github.com/facebookincubator/superconsole/blob/main/README.md)
- [Releases](https://github.com/facebookincubator/superconsole/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/facebookincubator-superconsole
