Elixir bindings for the Rust ratatui terminal UI library.
This module provides the core API for building terminal UIs: initializing the terminal, drawing widgets, and polling for events — all via Rust NIFs that run on the BEAM's DirtyIo scheduler.
Quick start
ExRatatui.run(fn terminal ->
{w, h} = ExRatatui.terminal_size()
paragraph = %ExRatatui.Widgets.Paragraph{text: "Hello!"}
rect = %ExRatatui.Layout.Rect{x: 0, y: 0, width: w, height: h}
ExRatatui.draw(terminal, [{paragraph, rect}])
ExRatatui.poll_event(60_000)
end)Core functions
run/1— initialize the terminal, run a function, restore on exitdraw/2— render a list of{widget, rect}tuples in a single framepoll_event/1— non-blocking event polling (keyboard, mouse, resize)terminal_size/0— current terminal dimensions
OTP apps
For supervised TUI applications, see ExRatatui.App — a behaviour with
LiveView-inspired callbacks (mount/1, render/2, handle_event/2).
Running over SSH
To serve a TUI to remote clients without a local terminal, see
ExRatatui.SSH.Daemon and the SSH transport guide.
Widgets
See ExRatatui.Widgets.Paragraph, ExRatatui.Widgets.Block,
ExRatatui.Widgets.List, ExRatatui.Widgets.Table,
ExRatatui.Widgets.Gauge, ExRatatui.Widgets.LineGauge,
ExRatatui.Widgets.Tabs, ExRatatui.Widgets.Scrollbar,
ExRatatui.Widgets.Checkbox, ExRatatui.Widgets.TextInput,
ExRatatui.Widgets.Clear, ExRatatui.Widgets.Markdown,
ExRatatui.Widgets.Textarea, ExRatatui.Widgets.Throbber,
ExRatatui.Widgets.Popup, and ExRatatui.Widgets.WidgetList.
Testing
Use init_test_terminal/2 and get_buffer_content/1 for headless
rendering verification in CI — no TTY required.
Summary
Functions
Draws a list of {widget, rect} tuples to the terminal in a single frame.
Returns the test terminal's buffer contents as a string.
Initializes a headless test terminal with the given dimensions.
Polls for terminal events with a timeout (default 250ms).
Runs a TUI application.
Returns the current terminal size as {width, height}.
Returns the current cursor position from the TextInput state.
Returns the current text value from the TextInput state.
Forwards a key event to the TextInput state.
Creates a new TextInput state.
Sets the text value on the TextInput state.
Returns the cursor position as {row, col} from the Textarea state.
Returns the current text from the Textarea as a string (lines joined with \n).
Forwards a key event with modifiers to the Textarea state.
Returns the number of lines in the Textarea state.
Creates a new Textarea state.
Sets the text value on the Textarea state.
Types
@type terminal_ref() :: reference()
@type widget() :: ExRatatui.Widgets.Paragraph.t() | ExRatatui.Widgets.Block.t() | ExRatatui.Widgets.Checkbox.t() | ExRatatui.Widgets.Clear.t() | ExRatatui.Widgets.List.t() | ExRatatui.Widgets.Table.t() | ExRatatui.Widgets.Gauge.t() | ExRatatui.Widgets.LineGauge.t() | ExRatatui.Widgets.Tabs.t() | ExRatatui.Widgets.Scrollbar.t() | ExRatatui.Widgets.Markdown.t() | ExRatatui.Widgets.Popup.t() | ExRatatui.Widgets.Textarea.t() | ExRatatui.Widgets.TextInput.t() | ExRatatui.Widgets.Throbber.t() | ExRatatui.Widgets.WidgetList.t()
Functions
@spec draw(terminal_ref(), [{widget(), ExRatatui.Layout.Rect.t()}]) :: :ok | {:error, term()}
Draws a list of {widget, rect} tuples to the terminal in a single frame.
Each tuple pairs a widget struct (e.g. %Paragraph{}, %Table{}) with a
%Rect{} that defines where to render it. Returns :ok on success or
{:error, reason} on failure.
Examples
iex> terminal = ExRatatui.init_test_terminal(40, 10)
iex> paragraph = %ExRatatui.Widgets.Paragraph{text: "Hello!"}
iex> rect = %ExRatatui.Layout.Rect{x: 0, y: 0, width: 40, height: 10}
iex> ExRatatui.draw(terminal, [{paragraph, rect}])
:ok
@spec get_buffer_content(terminal_ref()) :: String.t() | {:error, term()}
Returns the test terminal's buffer contents as a string.
Each line is trimmed of trailing whitespace and joined with newlines.
Only works with a test terminal reference from init_test_terminal/2.
Examples
iex> terminal = ExRatatui.init_test_terminal(20, 3)
iex> paragraph = %ExRatatui.Widgets.Paragraph{text: "Hi there"}
iex> rect = %ExRatatui.Layout.Rect{x: 0, y: 0, width: 20, height: 3}
iex> ExRatatui.draw(terminal, [{paragraph, rect}])
iex> ExRatatui.get_buffer_content(terminal) =~ "Hi there"
true
@spec init_test_terminal(non_neg_integer(), non_neg_integer()) :: terminal_ref() | {:error, term()}
Initializes a headless test terminal with the given dimensions.
Takes width (columns) and height (rows) for the virtual terminal.
Uses ratatui's TestBackend — no real terminal needed. Useful for testing
rendering output without a TTY. Returns a terminal reference.
Examples
iex> terminal = ExRatatui.init_test_terminal(40, 10)
iex> is_reference(terminal)
true
iex> terminal = ExRatatui.init_test_terminal(40, 10)
iex> alias ExRatatui.Widgets.Paragraph
iex> alias ExRatatui.Layout.Rect
iex> :ok = ExRatatui.draw(terminal, [{%Paragraph{text: "Hello!"}, %Rect{x: 0, y: 0, width: 40, height: 10}}])
iex> ExRatatui.get_buffer_content(terminal) =~ "Hello!"
true
@spec poll_event(non_neg_integer()) :: ExRatatui.Event.t() | nil | {:error, term()}
Polls for terminal events with a timeout (default 250ms).
Returns an Event.Key, Event.Mouse, Event.Resize struct, nil
if no event within the timeout, or {:error, reason} on failure.
@spec run((terminal_ref() -> term())) :: term() | {:error, term()}
Runs a TUI application.
Initializes the terminal, calls fun with the terminal reference,
and ensures terminal cleanup on exit.
ExRatatui.run(fn terminal ->
# your TUI loop here
end)
@spec terminal_size() :: {non_neg_integer(), non_neg_integer()} | {:error, term()}
Returns the current terminal size as {width, height}.
Returns {:error, reason} if the terminal size cannot be determined.
@spec text_input_cursor(reference()) :: non_neg_integer()
Returns the current cursor position from the TextInput state.
Examples
iex> state = ExRatatui.text_input_new()
iex> ExRatatui.text_input_cursor(state)
0
iex> ExRatatui.text_input_handle_key(state, "a")
:ok
iex> ExRatatui.text_input_cursor(state)
1
Returns the current text value from the TextInput state.
Examples
iex> state = ExRatatui.text_input_new()
iex> ExRatatui.text_input_get_value(state)
""
Forwards a key event to the TextInput state.
Pass the code field from an ExRatatui.Event.Key struct. Supports
printable characters, "backspace", "delete", "left", "right",
"home", and "end".
Examples
iex> state = ExRatatui.text_input_new()
iex> ExRatatui.text_input_handle_key(state, "h")
:ok
iex> ExRatatui.text_input_handle_key(state, "i")
:ok
iex> ExRatatui.text_input_get_value(state)
"hi"
@spec text_input_new() :: reference()
Creates a new TextInput state.
Returns a reference to the Rust-side state (ResourceArc). Pass this
reference as the :state field of %ExRatatui.Widgets.TextInput{}.
Examples
iex> state = ExRatatui.text_input_new()
iex> is_reference(state)
true
Sets the text value on the TextInput state.
The cursor is moved to the end of the new value.
Examples
iex> state = ExRatatui.text_input_new()
iex> ExRatatui.text_input_set_value(state, "hello")
:ok
iex> ExRatatui.text_input_get_value(state)
"hello"
@spec textarea_cursor(reference()) :: {non_neg_integer(), non_neg_integer()}
Returns the cursor position as {row, col} from the Textarea state.
Examples
iex> state = ExRatatui.textarea_new()
iex> ExRatatui.textarea_cursor(state)
{0, 0}
Returns the current text from the Textarea as a string (lines joined with \n).
Examples
iex> state = ExRatatui.textarea_new()
iex> ExRatatui.textarea_get_value(state)
""
Forwards a key event with modifiers to the Textarea state.
modifiers is a list of active modifier strings (e.g. ["ctrl"]),
defaults to []. The textarea supports Emacs-style shortcuts
(Ctrl+Z undo, Ctrl+Y redo, etc.).
Examples
iex> state = ExRatatui.textarea_new()
iex> ExRatatui.textarea_handle_key(state, "h", [])
:ok
iex> ExRatatui.textarea_handle_key(state, "i", [])
:ok
iex> ExRatatui.textarea_get_value(state)
"hi"
@spec textarea_line_count(reference()) :: non_neg_integer()
Returns the number of lines in the Textarea state.
Examples
iex> state = ExRatatui.textarea_new()
iex> ExRatatui.textarea_line_count(state)
1
@spec textarea_new() :: reference()
Creates a new Textarea state.
Returns a reference to the Rust-side state (ResourceArc). Pass this
reference as the :state field of %ExRatatui.Widgets.Textarea{}.
Examples
iex> state = ExRatatui.textarea_new()
iex> is_reference(state)
true
Sets the text value on the Textarea state.
Examples
iex> state = ExRatatui.textarea_new()
iex> ExRatatui.textarea_set_value(state, "hello\nworld")
:ok
iex> ExRatatui.textarea_get_value(state)
"hello\nworld"