Quick lookup for every built-in widget and helper. One example per struct, key fields inline. For concepts see Building UIs; for full option shapes, the module docs.
Styles & rich text
Style
%Style{
fg: :green, # or {r,g,b}, or 0..255 index
bg: :reset,
modifiers: [:bold, :italic, :underlined, :reversed, :dim]
}Colors accept :reset, named atoms, {r, g, b}, or 0–255. Modifiers are additive.
Rich text spans/lines
alias ExRatatui.Text.{Line, Span}
Line.new([
Span.new(" ok ", style: %Style{fg: :green, modifiers: [:bold]}),
Span.new(" build passed", style: %Style{fg: :gray})
])Accepted by Paragraph.text, List.items, Table.rows/header, Tabs.titles, Block.title.
Layout
Layout.split/3
alias ExRatatui.Layout
[left, right] =
Layout.split(frame_rect, :horizontal, [
{:percentage, 30},
{:percentage, 70}
])Direction: :horizontal or :vertical. Constraints: :length, :percentage, :min, :max, :ratio.
%Rect{}
%ExRatatui.Layout.Rect{x: 0, y: 0, width: 80, height: 24}All widgets render into a Rect. Returned from Layout.split/3, derived from %Frame{}, or built manually for absolute placement.
Text & content
Paragraph
%Paragraph{
text: "Hello!", # String | Span | Line | [Span]
style: %Style{fg: :green},
alignment: :center, # :left | :center | :right
wrap: true,
scroll: {0, 0}, # {vertical, horizontal}
block: %Block{title: " Info ", borders: [:all]}
}Markdown
%Markdown{
content: "# Title\n\n**bold** *italic* `code`\n\n- a\n- b",
wrap: true,
scroll: {0, 0},
block: %Block{borders: [:all]}
}Supports headings, bold/italic, inline code, lists.
Lists, tables & scrolling
List
%List{
items: ["Item 1", "Item 2", "Item 3"],
selected: 0,
highlight_style: %Style{fg: :yellow, modifiers: [:bold]},
highlight_symbol: ">> ",
block: %Block{title: " Menu ", borders: [:all]}
}Table
%Table{
header: ["Name", "Score"],
rows: [["Alice", "95"], ["Bob", "87"]],
widths: [{:percentage, 70}, {:percentage, 30}],
selected: 0,
highlight_style: %Style{fg: :yellow},
column_spacing: 1,
block: %Block{borders: [:all]}
}WidgetList
%WidgetList{
items: [
{%Paragraph{text: "Row 1"}, 3}, # {widget, height}
{%Paragraph{text: "Row 2"}, 5}
],
selected: 0,
scroll_offset: 0,
highlight_style: %Style{bg: {50, 50, 50}}
}Row-clipped scroll. Good for 1k+ items; only visible rows cross the NIF boundary.
Scrollbar
%Scrollbar{
orientation: :vertical_right, # or :vertical_left, :horizontal_bottom, :horizontal_top
content_length: 100,
position: 25,
viewport_content_length: 20,
thumb_style: %Style{fg: :yellow}
}Progress & activity
Gauge
%Gauge{
ratio: 0.65, # 0.0..1.0
label: "65%",
gauge_style: %Style{fg: :green},
block: %Block{borders: [:all]}
}LineGauge
%LineGauge{
ratio: 0.4,
label: "Loading...",
filled_style: %Style{fg: :blue},
unfilled_style: %Style{fg: :dark_gray}
}Single-cell-tall bar — great for compact status rows.
Sparkline
%Sparkline{
data: [0, 1, 3, 5, 8, nil, 2, 4], # nil = absent
max: 8, # nil auto-scales
direction: :left_to_right,
bar_set: :nine_levels,
style: %Style{fg: :cyan}
}Throbber
%Throbber{
label: "Loading",
throbber_set: :braille, # :braille | :dots | :line | :ascii
step: state.tick, # caller increments
throbber_style: %Style{fg: :cyan}
}Drive with Subscription.interval/3 (reducer) or Process.send_after/3 (callback).
BarChart
alias ExRatatui.Widgets.Bar
%BarChart{
data: [
%Bar{label: "Mon", value: 42},
%Bar{label: "Tue", value: 67, style: %Style{fg: :yellow}}
],
bar_width: 5,
bar_gap: 1,
direction: :vertical, # or :horizontal
max: 100 # nil auto-scales
}Use :groups with %BarGroup{} for side-by-side clusters (mutually exclusive with :data).
Input
TextInput
state = ExRatatui.text_input_new() # mount once, keep in app state
%TextInput{
state: state,
placeholder: "Type here...",
style: %Style{fg: :white},
cursor_style: %Style{bg: :white, fg: :black},
block: %Block{borders: [:all]}
}
ExRatatui.text_input_handle_key(state, key_event)
value = ExRatatui.text_input_get_value(state)Textarea
state = ExRatatui.textarea_new()
%Textarea{
state: state,
placeholder: "Enter message...",
cursor_line_style: %Style{bg: {40, 40, 40}},
line_number_style: %Style{fg: :dark_gray}, # nil hides line numbers
block: %Block{borders: [:all]}
}
ExRatatui.textarea_handle_key(state, key_event)
value = ExRatatui.textarea_get_value(state)Enter = newline, Ctrl+Enter = submit.
Checkbox
%Checkbox{
label: "Enable notifications",
checked: true,
checked_style: %Style{fg: :green},
checked_symbol: "[x]",
unchecked_symbol: "[ ]"
}SlashCommands
alias ExRatatui.Widgets.SlashCommands
alias ExRatatui.Widgets.SlashCommands.Command
commands = [
%Command{name: "help", description: "Show help", aliases: ["h", "?"]},
%Command{name: "quit", description: "Exit", aliases: ["q"]}
]
case SlashCommands.parse(input_text) do
{:slash, query} -> SlashCommands.match_commands(commands, query)
:no_slash -> []
endPair the matches with a %Popup{} in render/2 to show autocomplete.
Charts & canvas
Chart
alias ExRatatui.Widgets.Chart.{Axis, Dataset}
%Chart{
datasets: [
%Dataset{
name: "CPU",
data: [{0.0, 12.0}, {1.0, 25.0}, {2.0, 48.0}],
marker: :braille,
graph_type: :line, # :line | :scatter | :bar
style: %Style{fg: :cyan}
}
],
x_axis: %Axis{title: "Time", bounds: {0.0, 3.0}, labels: ["0", "1", "2", "3"]},
y_axis: %Axis{title: "Usage %", bounds: {0.0, 100.0}, labels: ["0", "50", "100"]},
legend_position: :top_right # nil hides
}Canvas
alias ExRatatui.Widgets.Canvas.{Circle, Label, Line, Points, Rectangle}
%Canvas{
x_bounds: {0.0, 100.0},
y_bounds: {0.0, 50.0},
marker: :braille, # :braille | :dot | :block | :bar | :half_block
background_color: :black,
shapes: [
%Line{x1: 0.0, y1: 0.0, x2: 100.0, y2: 50.0, color: :cyan},
%Rectangle{x: 10.0, y: 10.0, width: 30.0, height: 20.0, color: :yellow},
%Circle{x: 70.0, y: 25.0, radius: 10.0, color: :magenta},
%Points{coords: [{20.0, 40.0}, {50.0, 30.0}], color: :green},
%Label{x: 70.0, y: 25.0, text: "★", color: :white}
]
}World map
alias ExRatatui.Widgets.Canvas.Map, as: CanvasMap
%Canvas{
x_bounds: {-180.0, 180.0},
y_bounds: {-90.0, 90.0},
marker: :dot,
shapes: [
%CanvasMap{resolution: :high, color: :green},
%Label{x: -74.0, y: 40.7, text: "NYC", color: :yellow}
]
}Containers & overlays
Block
%Block{
title: " Panel ", # String | Span | Line | [Span]
borders: [:all], # or [:top, :left, ...]
border_type: :rounded, # :plain | :rounded | :double | :thick
border_style: %Style{fg: :cyan},
padding: {0, 0, 0, 0} # {left, right, top, bottom}
}Wraps another widget via that widget's :block field.
Popup
%Popup{
content: %Paragraph{text: "Are you sure?"},
percent_width: 50, # or fixed_width: 40
percent_height: 30, # or fixed_height: 10
block: %Block{title: " Confirm ", borders: [:all], border_type: :double}
}Renders centered on top of whatever's already drawn.
Tabs
%Tabs{
titles: ["Overview", "Details", "Settings"],
selected: 0,
highlight_style: %Style{fg: :yellow, modifiers: [:bold]},
divider: " | ",
padding: {1, 1},
block: %Block{borders: [:bottom]}
}Clear
%Clear{}Fills the rect with blank cells. Use before a popup to dim underlying content.
Calendar
%Calendar{
display_date: ~D[2026-03-15],
events: [
{~D[2026-03-10], %Style{fg: :red, modifiers: [:bold]}},
{~D[2026-03-20], %Style{fg: :green}}
],
default_style: %Style{fg: :white},
show_month_header: true,
header_style: %Style{fg: :yellow, modifiers: [:bold]},
show_weekdays_header: true,
weekday_style: %Style{fg: :cyan},
show_surrounding: %Style{fg: :dark_gray}, # nil hides surrounding days
block: %Block{title: " March ", borders: [:all]}
}Needs ~22×8 cells (24×10 with a block). events accepts a keyword-like list of {%Date{}, %Style{}} tuples or a %{Date => Style} map.