I’ve been working on a TUI framework for Elixir called Drafter, inspired by Python’s Textual but built around Elixir idioms — declarative rendering, pattern-matched event handling, and OTP-native architecture.
The basic idea:
Mix.install([{:drafter, "~> 0.1.3"}])
defmodule CounterApp do
use Drafter.App
def mount(_props), do: %{count: 0}
def render(state) do
vertical([
header("Counter"),
label("Count: #{state.count}", flex: 1),
horizontal([
button("−", on_click: :dec),
button("+", on_click: :inc)
], gap: 2),
footer(bindings: [{"q", "Quit"}])
])
end
def handle_event(:inc, _data, state), do: {:ok, %{state | count: state.count + 1}}
def handle_event(:dec, _data, state), do: {:ok, %{state | count: state.count - 1}}
def handle_event({:key, :q}, _state), do: {:stop, :normal}
def handle_event(_event, state), do: {:noreply, state}
end
Drafter.run(CounterApp)
What’s in the box:
- 30+ widgets — DataTable, Tree, Charts, TextInput, TextArea, Checkbox, RadioSet, Markdown, CodeView, and more
- Flexible layouts — vertical, horizontal, grid, scrollable, sidebar
- Multi-screen navigation — push/pop screens, modals, popovers, panels, toasts
- Theming system with HSL/RGB/hex color support
- Declarative widget event handling with automatic focus management
- DOM-like three-phase event system (capture → target → bubble)
- Animation engine with 30+ easing functions
- Syntax highlighting via tree-sitter (optional)
- Headless testing harness for ExUnit
- Many examples
TUI over SSH:
Drafter.Server.start_ssh(ChatApp,
port: 2222,
mode: :shared,
auth: [{"alice", "pass"}, {"bob", "pass"}]
)
Any standard SSH client connects and gets a full interactive TUI session. :shared mode means all connected clients see the same state — input from any client updates everyone’s view in real time. The included ssh_chat.exs example is a working multi-user chat app you can try in a couple of minutes.
Built on OTP 28 — raw terminal mode, proper Unicode, and lazy input reading. Earlier OTP versions won’t work correctly.
Feedback and contributions welcome. Still early, but it’s been running real apps reliably.






















