AppCUI is a fast, cross-platform console and text-based user interface (CUI/TUI) framework for Rust. It combines a low-level console engine for input (mouse, keyboard, clipboard, etc.), colors, and rendering with a high-level, rich toolkit of widgets such as windows, menus, buttons, checkboxes, and many more, available for Windows, Linux and Mac.
See the code
⯈ 𝗔𝗽𝗽𝗖𝗨𝗜-𝗿𝘀 🖳
AppCUI-rs is a fast, cross-platform Rust library for building modern, text-based user interfaces (TUIs) with rich widgets, themes, and full Unicode support—an alternative to ncurses and other terminal UI frameworks.
👉 Check out the Gallery for full demos of all controls!
AppCUI-rs ships with a rich set of controls and widgets for building terminal user interfaces. The full list of stock controls is documented here.
AppCUI supports various backends depending on the operating system:
crossterm crate, enabled via a feature flagMore on the supported backends can be found here
Add the following to your Cargo.toml:
[dependencies]
appcui = "0.5"
Then create a new Rust project and add the following code:
use appcui::prelude::*;
fn main() -> Result<(), appcui::system::Error> {
App::new()
.window(|| {
let mut win = Window::new(
"Test",
LayoutBuilder::new().alignment(Alignment::Center).width(30).height(9).build(),
window::Flags::Sizeable,
);
win.add(Label::new(
"Hello World !",
LayoutBuilder::new().alignment(Alignment::Center).width(13).height(1).build(),
));
win
})
.run()
}
Or a more compact version using proc-macros:
use appcui::prelude::*;
fn main() -> Result<(), appcui::system::Error> {
App::new()
.window(|| {
let mut win = window!("Test,a:c,w:30,h:9");
win.add(label!("'Hello World !',a:c,w:13,h:1"));
win
})
.run()
}
Alternatively, you can use the frame-based or input-based mode to create applications that can access the screen directly (without needing the entire UI architecture):
use appcui::prelude::*;
struct HelloWorld;
impl InputApp for HelloWorld {
fn on_paint(&self, surface: &mut Surface) {
surface.write_string(0, 0, "Hello World !", charattr!("white"), false);
}
}
fn main() -> Result<(), appcui::system::Error> {
App::input_app(HelloWorld {}).run()
}
Then run the project with cargo run. You should see a window with the title Test and the text Hello World ! in the center.
AppCUI-rs comes with a set of examples to help you get started. You can find them in the examples folder, including:
An example that creates a window with a button that, when pressed, increases a counter.
use appcui::prelude::*;
// Create a window that handles button events and has a counter
#[Window(events = ButtonEvents)]
struct CounterWindow {
counter: i32
}
impl CounterWindow {
fn new() -> Self {
let mut w = Self {
// set up the window title and position
base: window!("'Counter window',a:c,w:30,h:5"),
// initial counter is 1
counter: 1
};
// add a single button with the caption "1" (like the counter)
w.add(button!("'1',d:b,w:20"));
w
}
}
impl ButtonEvents for CounterWindow {
// When the button is pressed, this function will be called
// with the handle of the button that was pressed
// Since we only have one button, we don't need to store its handle
// in the struct, as we will receive the handle via the on_pressed method
fn on_pressed(&mut self, handle: Handle<Button>) -> EventProcessStatus {
// increase the counter
self.counter += 1;
// create a text that contains the new counter
let text = format!("{}",self.counter);
// acquire a mutable reference to the button using its handle
if let Some(button) = self.control_mut(handle) {
// set the caption of the button to the new text
button.set_caption(&text);
}
// Tell the AppCUI framework that we have processed this event
// This allows AppCUI to repaint the button
EventProcessStatus::Processed
}
}
fn main() -> Result<(), appcui::system::Error> {
// create a new application, add a CounterWindow, and start the event loop
App::new().window(|| CounterWindow::new()).run()
}
Contributions, issues, and feature requests are welcome!
Check out CONTRIBUTING.md to get started.
Join the discussion in GitHub Discussions.
5,477 followers · starred Sep 2025
143 followers · starred Sep 2025
84 followers · starred Jul 2025
334 followers · starred Sep 2025
AppCUI is a fast, cross-platform console and text-based user interface (CUI/TUI) framework for Rust. It combines a low-level console engine for input (mouse, keyboard, clipboard, etc.), colors, and rendering with a high-level, rich toolkit of widgets such as windows, menus, buttons, checkboxes, and many more, available for Windows, Linux and Mac.
See the code
⯈ 𝗔𝗽𝗽𝗖𝗨𝗜-𝗿𝘀 🖳
AppCUI-rs is a fast, cross-platform Rust library for building modern, text-based user interfaces (TUIs) with rich widgets, themes, and full Unicode support—an alternative to ncurses and other terminal UI frameworks.
👉 Check out the Gallery for full demos of all controls!
AppCUI-rs ships with a rich set of controls and widgets for building terminal user interfaces. The full list of stock controls is documented here.
AppCUI supports various backends depending on the operating system:
crossterm crate, enabled via a feature flagMore on the supported backends can be found here
Add the following to your Cargo.toml:
[dependencies]
appcui = "0.5"
Then create a new Rust project and add the following code:
use appcui::prelude::*;
fn main() -> Result<(), appcui::system::Error> {
App::new()
.window(|| {
let mut win = Window::new(
"Test",
LayoutBuilder::new().alignment(Alignment::Center).width(30).height(9).build(),
window::Flags::Sizeable,
);
win.add(Label::new(
"Hello World !",
LayoutBuilder::new().alignment(Alignment::Center).width(13).height(1).build(),
));
win
})
.run()
}
Or a more compact version using proc-macros:
use appcui::prelude::*;
fn main() -> Result<(), appcui::system::Error> {
App::new()
.window(|| {
let mut win = window!("Test,a:c,w:30,h:9");
win.add(label!("'Hello World !',a:c,w:13,h:1"));
win
})
.run()
}
Alternatively, you can use the frame-based or input-based mode to create applications that can access the screen directly (without needing the entire UI architecture):
use appcui::prelude::*;
struct HelloWorld;
impl InputApp for HelloWorld {
fn on_paint(&self, surface: &mut Surface) {
surface.write_string(0, 0, "Hello World !", charattr!("white"), false);
}
}
fn main() -> Result<(), appcui::system::Error> {
App::input_app(HelloWorld {}).run()
}
Then run the project with cargo run. You should see a window with the title Test and the text Hello World ! in the center.
AppCUI-rs comes with a set of examples to help you get started. You can find them in the examples folder, including:
An example that creates a window with a button that, when pressed, increases a counter.
use appcui::prelude::*;
// Create a window that handles button events and has a counter
#[Window(events = ButtonEvents)]
struct CounterWindow {
counter: i32
}
impl CounterWindow {
fn new() -> Self {
let mut w = Self {
// set up the window title and position
base: window!("'Counter window',a:c,w:30,h:5"),
// initial counter is 1
counter: 1
};
// add a single button with the caption "1" (like the counter)
w.add(button!("'1',d:b,w:20"));
w
}
}
impl ButtonEvents for CounterWindow {
// When the button is pressed, this function will be called
// with the handle of the button that was pressed
// Since we only have one button, we don't need to store its handle
// in the struct, as we will receive the handle via the on_pressed method
fn on_pressed(&mut self, handle: Handle<Button>) -> EventProcessStatus {
// increase the counter
self.counter += 1;
// create a text that contains the new counter
let text = format!("{}",self.counter);
// acquire a mutable reference to the button using its handle
if let Some(button) = self.control_mut(handle) {
// set the caption of the button to the new text
button.set_caption(&text);
}
// Tell the AppCUI framework that we have processed this event
// This allows AppCUI to repaint the button
EventProcessStatus::Processed
}
}
fn main() -> Result<(), appcui::system::Error> {
// create a new application, add a CounterWindow, and start the event loop
App::new().window(|| CounterWindow::new()).run()
}
Contributions, issues, and feature requests are welcome!
Check out CONTRIBUTING.md to get started.
Join the discussion in GitHub Discussions.
5,477 followers · starred Sep 2025
143 followers · starred Sep 2025
84 followers · starred Jul 2025
334 followers · starred Sep 2025