mcass19

mcass19

ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applications that run under OTP supervision — without blocking the BEAM.

Why?

I wanted to build terminal UIs in Elixir with the same ergonomics we’re used to from LiveView. The existing options in the ecosystem are either stale (ratatouille hasn’t been updated in years) or focused on CLI output rather than full-screen interactive apps. Meanwhile, ratatui is one of the most actively maintained TUI libraries in any language — so bridging it to Elixir felt like the right approach.

What it looks like

The ExRatatui.App behaviour uses LiveView-inspired callbacks — mount/1, render/2, handle_event/2, and handle_info/2:

defmodule MyCounter do
  use ExRatatui.App

  @impl true
  def mount(_opts), do: {:ok, %{count: 0}}

  @impl true
  def render(state, frame) do
    alias ExRatatui.Widgets.Paragraph
    alias ExRatatui.Layout.Rect

    widget = %Paragraph{text: "Count: #{state.count}"}
    [{widget, %Rect{x: 0, y: 0, width: frame.width, height: frame.height}}]
  end

  @impl true
  def handle_event(%ExRatatui.Event.Key{code: "up"}, state),
    do: {:noreply, %{state | count: state.count + 1}}

  def handle_event(%ExRatatui.Event.Key{code: "q"}, state),
    do: {:stop, state}

  def handle_event(_event, state),
    do: {:noreply, state}
end

# Add to your supervision tree
children = [{MyCounter, []}]
Supervisor.start_link(children, strategy: :one_for_one)

Features

  • 5 widgets (so far): Paragraph, Block, List, Table, Gauge — with Block composition on all of them
  • Constraint-based layout engine — split areas by percentage, length, min, max, or ratio
  • Non-blocking event polling — keyboard, mouse, and resize events on BEAM’s DirtyIo scheduler
  • OTP-supervised apps via ExRatatui.App behaviour
  • Full color support — 17 named colors, RGB, and 256-color indexed
  • Headless test backend — render to an in-memory buffer for CI-friendly testing
  • Precompiled NIF binaries for Linux, macOS, and Windows — no Rust toolchain needed

Installation

def deps do
  [{:ex_ratatui, "~> 0.4"}]
end

Precompiled binaries are downloaded automatically. No Rust toolchain required.

Examples

The repo includes several examples you can run directly:

  • mix run examples/hello_world.exs — minimal paragraph display
  • mix run examples/counter.exs — interactive counter with key events
  • mix run examples/counter_app.exs — counter using the App behaviour
  • mix run examples/system_monitor.exs — system dashboard (CPU, memory, disk, network, BEAM stats)
  • mix run examples/task_manager.exs — full task manager using all widgets
  • examples/task_manager/ — a complete supervised Ecto + SQLite CRUD app with a TUI interface

What’s next

More widgets (Tabs, Sparkline, BarChart, Scrollbar), rich text primitives (mixed-style spans within a single widget), custom widgets. Beyond that — a theming system, periodic handle_tick callbacks, viewport modes for inline rendering, single-binary distribution via Burrito, etc etc.

The precompiled NIFs already target ARM and RISC-V, so running TUI apps on Nerves devices over SSH should be a natural fit? I haven’t played with it yet!

The issues list is the place to go — ratatui has a huge surface area, so there’s a lot of room to grow.

Contributions are very welcome!!!

Links

I’d love to hear what people think and what you’d want to build with it.

https://github.com/mcass19/ex_ratatui

Showing Posts 1 to 10

dbaer

dbaer

Love this! After using IBM AS400 at work I had the thought of a TUI over SSH and this library looks like it fits the bill (DynamicSupervisor with Erlang’s built in ssh server?)

Ran into a minor crash with the task_manager.exs example, but it was an easy fix and I’ve submitted a PR :smiley:

mcass19

mcass19 OP

Quick update! ExRatatui is now at v0.5 with more widgets. And more in the makings…

Some projects built with it are starting to appear:

  • AshTui — Interactive terminal explorer for Ash domains, resources, attributes, actions, and relationships. Two-panel UI with search, tabs, scrollbar, and relationship navigation. Run mix ash.tui and you’re in.
  • Nerves ExRatatui Example — System monitor and LED control TUI running on a Raspberry Pi. Renders directly to the HDMI console. Works on RPi Zero, 3, 4, and 5.

Feedback and contributions very welcome!

vegabook

vegabook

Looks fantastic. Looking forward to the charting aspects being added especially the line charts.

mcass19

mcass19 OP

0.6 is out! Headline is a built-in SSH transport that lets you serve any ExRatatui.App module as a remote TUI over OTP :ssh. A single daemon hands each connected client its own isolated session; multiple clients can attach to the same app at the same time without stepping on each other.

What it looks like

The simplest shape. Drop the daemon straight into a supervision tree:

children = [
  {MyApp.TUI,
   transport: :ssh,
   port: 2222,
   auto_host_key: true,
   auth_methods: ~c"password",
   user_passwords: [{~c"admin", ~c"admin"}]}
]

Then from any other machine:

ssh -p 2222 admin@localhost

That’s it. transport: :ssh on ExRatatui.App routes start_link/1 through a new ExRatatui.SSH.Daemon instead of the local terminal path. The app module itself is unchange. It doesn’t know it’s being served over SSH. auto_host_key: true is the other nice bit. More on that on the docs.

Example: phoenix_ex_ratatui_example.

Integrating with nerves_ssh

If you’re already running nerves_ssh on a Nerves device you don’t need a second daemon. ExRatatui.SSH is an :ssh_server_channel, and nerves_ssh takes one through its subsystems: list. There’s a helper for the tuple shape:

config :nerves_ssh,
  authorized_keys: [File.read!("/root/.ssh/authorized_keys")],
  subsystems: [
    :ssh_sftpd.subsystem_spec(cwd: ~c"/"),
    ExRatatui.SSH.subsystem(MyApp.TUI)
  ]

Connect with:

ssh -t nerves.local -s Elixir.MyApp.TUI

The -t is required — OpenSSH doesn’t allocate a PTY by default for subsystem invocations (sftp and similar binary protocols don’t need one), so without it your local terminal stays in cooked mode and keystrokes get line-buffered and locally echoed over the TUI. The subsystem name is the full Elixir module name as a charlist, so two different app modules configured into the same daemon get distinct names and don’t collide.

Example: nerves_ex_ratatui_example.

What’s next

Reducer runtime for non-trivial apps. More distribution capabilites. More widgets. Contributions and TUIs out there are starting to appear :)!

ademenev

ademenev

Maybe this is a shortcoming of the rust library, but support for double-width characters widely used in CJK languages is not complete. For example, if in text input there are some double-width characters then when pressing right arrow when the cursor is at the end of input makes the cursor not visible

mcass19

mcass19 OP

Hi @ademenev , thanks for the report! You’re right, this is a real gap on our side, not a ratatui shortcoming.

Ratatui handles wide characters correctly; our TextInput widget tracks the cursor and viewport in character counts while the widget width is in terminal cells, so the two drift apart as soon as any CJK character shows up.

I’ve opened an issue to track it here: Text input: cursor becomes invisible at end when line contains double-width (CJK) characters · Issue #45 · mcass19/ex_ratatui · GitHub

I’d be more than happy if you want to take a look and contribute to it. Otherwise I’ll pick it up soon. Either way, really appreciate your comment :).

mcass19

mcass19 OP

Hi all! A bunch of stuff shipped since the last update.

Highlights:

  • Reducer runtime: Elm-style update/2 + commands + subscriptions, alongside the original callback runtime. Pick whichever fits your app.
  • New widgets: Chart, Canvas, Calendar, Sparkline, BarChart. Plus a Widget protocol so you can build composite widgets in pure Elixir, a Focus primitive for multi-panel apps, and rich text (Span / Line) across every text-bearing widget.
  • SSH transport: subsystem mode, nerves_ssh integration, auto host-key bootstrap. Drop the daemon into a supervision tree and you’re in.
  • Distribution-attach transport: serve any ExRatatui.App to remote BEAM nodes over Erlang distribution.
  • Telemetry across the runtime (mount, events, frames, transport handshakes), and a public Transport behaviour for plugging in custom byte-stream carriers.
  • Livebook: kino_ex_ratatui runs TUIs straight from a notebook. Honestly more fun than I expected :D!
  • New guides covering everything from setup to advanced topics. Starting point: Getting Started — ExRatatui v0.11.1

Readme, changelog, and examples have everything else: GitHub - mcass19/ex_ratatui: Elixir bindings for the Rust ratatui terminal UI library · GitHub .

Would love to hear your feedback and what you end up building with it.

johns10davenport

johns10davenport

I’ll reach for this next time I need tui. I did a bit of ratatouille previously and it was not bad, but definitely could feel the staleness.

mcass19

mcass19 OP

Hi everyone! v0.10 is out.

Highlights:

  • Image rendering: PNG / JPEG / GIF / WebP / BMP via ExRatatui.Widgets.Image, protocol-aware across every transport: pixel-perfect Kitty / Sixel / iTerm2 graphics locally, halfblocks fallback over SSH or in Livebook, and it even renders over Erlang distribution.
  • BigText: oversized 8×8 pixel text for slide titles, splash screens, and banners.
  • CodeBlock: syntax-highlighted source via syntect, with curated themes, an optional line-number gutter, and bundled Elixir syntax.
  • Theming: ExRatatui.Theme, a pure-data palette struct with semantic slots, default/0 / light/0 constructors, and style helpers. No globals; apps thread it through render code.
  • Richer input: opt-in mouse capture, terminal focus events, and bracketed paste (a paste lands as a single %Event.Paste{} instead of being shredded across keystrokes). ExRatatui.Focus gained mouse routing, so a click focuses the panel under the cursor.
  • Layout & widget polish: multi-title Blocks, eight new border types, Flex modes + {:fill, weight} + segment spacing in layouts, Table footers and column/cell highlighting, List direction / scroll padding, Style :underline_color, set_terminal_title/1, and more.

Ecosystem:

  • bb_tui: a terminal dashboard for Beam Bots robots, now released after @jimsynz blessing.
  • kino_ex_ratatu: TUIs in Livebook notebooks; image rendering works there too.
  • phoenix_ex_ratatui: run TUIs in the browser inside Phoenix LiveView, powered by the new non-terminal CellSession renderer. Quite crazy :smiley:
  • tuis for the badge: on the same crazy boat with CellSession, a WIP PR that puts ex_ratatui TUIs on a Nerves e-ink badge, rendering the cell grid straight to the display.

README, changelog, and examples have everything else: GitHub - mcass19/ex_ratatui: Elixir bindings for the Rust ratatui terminal UI library · GitHub

As always, would love to hear feedback and what you build with it. Contributions extremely welcome everywhere!

jimsynz

jimsynz

Ash Core Team

I cannot overstate how amazing `bb_tui` is. I am super grateful @mcass19.

Where Next? Top

Trending in Announcing Top

woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
woylie
I released Doggo, a collection of unstyled Phoenix components. https://github.com/woylie/doggo Features Unstyled Phoenix components....
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
anuaralfetahe
Hello Published a new library - ProcessHub! ProcessHub is a library designed to manage process distribution within the Elixir cluster. ...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
sergio
It’s not that it’s vocabulary is too advanced. It’s something worse. I get lost trying to follow even a paragraph written by Claude. It’...
New
AstonJ
This showed up on my feed.. anyone heard of it? Just hype? Ox Alpha is a reasoning model designed for coding, sustained ag...
New
bartblast
Hey folks, I just published a post about Hologram’s funding and where the project goes next - the short version: Curiosum as Main Spons...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews