ExRatatui - Elixir bindings for the Rust ratatui terminal UI library

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.

17 Likes

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:

2 Likes