Widget Gallery

View Source

All widgets are available via the View DSL after use Raxol.Core.Runtime.Application. Layout containers (column, row, box) use do block syntax. Everything else is a plain function call.

To see them all running: mix raxol.playground (interactive demos across all categories).


Layout

Layout widgets arrange children on screen. These are the skeleton of every Raxol UI.

column

Vertical stack. Children are arranged top-to-bottom.

column style: %{gap: 1, padding: 1, align_items: :center} do
  [
    text("Header", style: [:bold]),
    divider(),
    text("Body content"),
    spacer(),
    text("Footer", style: [:dim])
  ]
end

Style options: gap, padding, align_items (:start, :center, :end, :stretch), flex, width, height.

row

Horizontal stack. Children are arranged left-to-right.

row style: %{gap: 2, align_items: :center} do
  [
    text("Status:"),
    text("Online", style: %{fg: :green, bold: true}),
    spacer(),
    button("Refresh", on_click: :refresh)
  ]
end

Same style options as column. Use spacer() to push items apart.

box

Container with optional border and padding. Good for grouping related content.

box style: %{border: :single, padding: 1, width: 40} do
  column style: %{gap: 1} do
    [
      text("User Profile", style: [:bold]),
      text("Name: #{model.name}"),
      text("Email: #{model.email}")
    ]
  end
end

Border styles: :single, :double, :rounded, :bold, :ascii.

spacer

Flexible space that fills available room. Useful for pushing items to opposite ends of a row or column.

row do
  [text("Left"), spacer(), text("Right")]
end

Options: size (integer, default 1), direction (:vertical or :horizontal).

divider

Horizontal line separator.

column do
  [text("Section A"), divider(), text("Section B")]
end

Options: char (string, default "-"), style.

split_pane

Resizable split layout with two panes.

split_pane(
  direction: :horizontal,
  ratio: {1, 2},
  min_size: 10,
  children: [left_panel, right_panel]
)

Options: direction (:horizontal or :vertical), ratio (tuple, default {1, 1}), min_size (integer, default 5).


Text & Display

Widgets for showing information to the user. These are all display-only -- no user interaction.

text

Styled text content. The most basic widget.

# Style atoms
text("Bold text", style: [:bold])
text("Dimmed", style: [:dim])
text("Warning", style: [:bold, :underline])

# Color via style map
text("Error", style: %{fg: :red, bold: true})
text("Success", style: %{fg: :green, bg: :black})

# With explicit fg/bg
text(content: "Custom", fg: :cyan, bg: :blue)

Style atoms: :bold, :dim, :italic, :underline, :strikethrough, :reverse.

Colors: :black, :red, :green, :yellow, :blue, :magenta, :cyan, :white, plus RGB tuples {r, g, b} and hex strings "#ff6600". Auto-downsampled to whatever the terminal supports.

label

Alias for text with an explicit content key.

label(content: "Field name:", style: %{bold: true})

progress

Progress bar indicator.

progress(value: 65, max: 100)

The underlying component module (Raxol.UI.Components.Display.Progress) supports more options when used directly: show_percentage, label, animated, width.

list

Render a list of items, with optional selection highlighting.

list(items: ["Elixir", "Rust", "Go", "Zig"])
list(items: model.todos, selected: model.selected_index)

table

Tabular data display with headers.

table(
  headers: ["Name", "Role", "Status"],
  rows: [
    ["Alice", "Admin", "Active"],
    ["Bob", "User", "Idle"],
    ["Carol", "User", "Active"]
  ]
)

The component module (Raxol.UI.Components.Display.Table) supports much more when used directly: column_widths (:auto or explicit list), border_style, sortable, filterable, selectable, striped, alignments. Handles keyboard navigation for row selection.

tree

Hierarchical tree view with expand/collapse. Component module only -- not available as a View DSL function.

alias Raxol.UI.Components.Display.Tree

nodes = [
  %{id: "src", label: "src/", children: [
    %{id: "lib", label: "lib/", children: [
      %{id: "app", label: "app.ex", children: []}
    ]},
    %{id: "test", label: "test/", children: []}
  ]}
]

# In init/1:
{:ok, tree_state} = Tree.init(%{id: "file_tree", nodes: nodes})

# In view/1 (render the tree state):
Tree.render(model.tree_state, context)

Keyboard: Up/Down (navigate), Right (expand), Left (collapse/go to parent), Enter/Space (select), Home/End (jump).

Options: indent_size, on_select, on_expand, on_collapse.

viewport

Scrollable container for content larger than the visible area. Component module only.

alias Raxol.UI.Components.Display.Viewport

{:ok, vp} = Viewport.init(%{
  id: "log_view",
  children: log_lines,
  visible_height: 20,
  show_scrollbar: true
})

# Scroll programmatically:
vp = Viewport.update({:scroll_by, 5}, vp)
vp = Viewport.update({:scroll_to, 0}, vp)

status_bar

Fixed status bar for key-value display. Component module only.

alias Raxol.UI.Components.Display.StatusBar

{:ok, bar} = StatusBar.init(%{
  id: "status",
  items: [
    %{key: "branch", label: "main"},
    %{key: "tests", label: "5484 passing"},
    %{key: "mode", label: "INSERT"}
  ],
  separator: " | "
})

code_block

Syntax-highlighted code display. Uses Makeup for Elixir highlighting, falls back to plain text for other languages. Component module only.

alias Raxol.UI.Components.CodeBlock

{:ok, block} = CodeBlock.init(%{
  content: ~s|defmodule Hello do\n  def world, do: :ok\nend|,
  language: "elixir"
})

markdown_renderer

Renders markdown text with terminal formatting. Supports headings, bold, italic, inline code, lists, and blockquotes. Component module only.

alias Raxol.UI.Components.MarkdownRenderer

{:ok, md} = MarkdownRenderer.init(%{
  markdown_text: "# Hello\n\nThis is **bold** and *italic*.\n\n- Item one\n- Item two",
  width: 60
})

Uses EarmarkParser when available, falls back to regex-based parsing.

image

Inline terminal image display. Supports Kitty, iTerm2, and Sixel protocols.

image(src: "logo.png", width: 30, height: 15)
image(src: raw_png_binary, protocol: :kitty, preserve_aspect: true)

Options: protocol (:kitty, :iterm2, :sixel -- auto-detected if omitted), preserve_aspect (default true).


Input

Widgets that accept user interaction. These handle keyboard events and fire callbacks.

button

Clickable button that sends a message to update/2 on press.

button("Save", on_click: :save)
button("Delete", on_click: {:delete, item.id})

The component module (Raxol.UI.Components.Input.Button) supports: role (:primary, :secondary, :danger, :success), disabled, shortcut, tooltip.

Keyboard: Enter or Space to activate.

text_input

Single-line text input field.

text_input(value: model.name, placeholder: "Enter name...")

The component module (Raxol.UI.Components.Input.TextInput) supports: on_change, on_submit, on_cancel, mask_char (for passwords), max_length, validator (function).

textarea

Multi-line text area.

textarea(
  value: model.notes,
  placeholder: "Write something...",
  rows: 8
)

For the full-featured editor with undo/redo, selection, and text wrapping, use the MultiLineInput component module directly.

checkbox

Toggle checkbox.

checkbox(checked: model.agreed, label: "I agree to the terms")

The component module (Raxol.UI.Components.Input.Checkbox) supports: on_toggle, disabled, required, tooltip.

Keyboard: Space or Enter to toggle.

radio_group

Radio button group for single selection from a set of options.

radio_group(
  options: ["Small", "Medium", "Large"],
  selected: model.size
)

Options: on_change.

select / select_list

Dropdown selection. The DSL select/1 creates a simple dropdown. The component module (Raxol.UI.Components.Input.SelectList) is a full-featured scrollable list with search, pagination, and multi-select.

# Simple DSL dropdown
select(
  options: ["Elixir", "Rust", "Go"],
  selected: model.language,
  placeholder: "Pick a language..."
)
# Full component with search and multi-select
alias Raxol.UI.Components.Input.SelectList

{:ok, sl} = SelectList.init(%{
  id: "lang_picker",
  options: [{"Elixir", :elixir}, {"Rust", :rust}, {"Go", :go}],
  enable_search: true,
  multiple: true,
  max_height: 10,
  on_select: :language_selected
})

Keyboard: Up/Down (navigate), Enter (select), PageUp/PageDown, Home/End, type to search.

tabs

Tab navigation bar.

tabs(tabs: ["Overview", "Details", "Settings"], active: model.active_tab)

The component module (Raxol.UI.Components.Input.Tabs) supports: on_change, keyboard Left/Right (with wrap), Home/End, 1-9 (direct select).

Nested dropdown/context menu with submenus. Component module only.

alias Raxol.UI.Components.Input.Menu

items = [
  %{id: :file, label: "File", children: [
    %{id: :new, label: "New", shortcut: "Ctrl+N"},
    %{id: :open, label: "Open", shortcut: "Ctrl+O"},
    %{id: :save, label: "Save", shortcut: "Ctrl+S", disabled: true}
  ]},
  %{id: :edit, label: "Edit", children: [
    %{id: :undo, label: "Undo", shortcut: "Ctrl+Z"},
    %{id: :redo, label: "Redo", shortcut: "Ctrl+Y"}
  ]}
]

{:ok, menu} = Menu.init(%{id: "main_menu", items: items, on_select: :menu_action})

Keyboard: Up/Down (skip disabled items), Right (open submenu), Left (close submenu), Enter (select), Escape (close).

multi_line_input

Full text editor with undo/redo, selection, and word wrapping. Component module only.

alias Raxol.UI.Components.Input.MultiLineInput

{:ok, editor} = MultiLineInput.init(%{
  id: "code_editor",
  value: "defmodule Hello do\n  def world, do: :ok\nend",
  width: 60,
  height: 20,
  wrap: :word
})

Options: wrap (:none, :char, :word), on_change, on_submit.

Features: cursor movement, shift-select, undo/redo history, line wrapping.


Overlay

Widgets that float above the main content.

Modal dialog that overlays the current view.

# Simple alert
modal(visible: model.show_confirm, title: "Confirm", content: text("Delete this item?"))

The component module (Raxol.UI.Components.Modal) supports multiple types:

alias Raxol.UI.Components.Modal

# Alert with buttons
{:ok, m} = Modal.init(%{
  id: "confirm",
  type: :alert,
  title: "Delete Item",
  content: "This cannot be undone.",
  buttons: [
    %{label: "Cancel", action: :cancel},
    %{label: "Delete", action: :delete}
  ]
})

# Prompt with input
{:ok, m} = Modal.init(%{
  id: "rename",
  type: :prompt,
  title: "Rename",
  input_value: model.current_name
})

# Form with validation
{:ok, m} = Modal.init(%{
  id: "settings",
  type: :form,
  title: "Settings",
  fields: [%{name: "timeout", label: "Timeout (ms)"}],
  validate: &validate_settings/1
})

Progress Indicators

Show how far along something is, or that work is happening.

progress (DSL)

Standard horizontal progress bar. This is the DSL entry point.

progress(value: 65, max: 100)

Progress.Bar, Progress.Spinner, Progress.Circular

Component modules for more control. Not separate DSL functions.

# Animated bar with percentage label
alias Raxol.UI.Components.Display.Progress

{:ok, bar} = Progress.init(%{
  id: "upload",
  progress: 0.65,
  width: 40,
  show_percentage: true,
  label: "Uploading...",
  animated: true
})
# Spinner (stateless utility -- call each frame)
alias Raxol.UI.Components.Progress.Spinner

# Available styles: :dots, :line, :circle, :arrow, :bounce,
#                   :pulse, :wave, :dots3, :square, :flip
frame = Spinner.spinner(:tick, 0, style: :dots, text: "Loading...")
ModuleUse case
Progress.BarDeterminate progress with known completion
Progress.SpinnerIndeterminate -- something is happening, unknown duration
Progress.CircularCircular/ring-style progress indicator

Charts

Streaming data visualization. All chart functions render braille or block characters and compose naturally in view/1.

sparkline

Minimal inline chart -- a line with no axes or legend.

sparkline(data: model.cpu_history, width: 30, height: 3, color: :green)

line_chart

Braille line chart with multi-series support.

line_chart(
  series: [
    %{name: "CPU", data: model.cpu_history, color: :cyan},
    %{name: "Memory", data: model.mem_history, color: :magenta}
  ],
  width: 60,
  height: 15,
  show_axes: true,
  show_legend: true
)

bar_chart

Block-character bar chart. Vertical or horizontal, grouped multi-series.

bar_chart(
  series: [
    %{name: "Q1", data: [42, 67, 55], color: :blue},
    %{name: "Q2", data: [50, 72, 61], color: :green}
  ],
  width: 50,
  height: 12,
  orientation: :vertical,
  show_values: true,
  show_legend: true
)

Options: bar_gap (gap within group), group_gap (gap between groups).

scatter_chart

Braille 2D scatter plot.

scatter_chart(
  series: [
    %{name: "Cluster A", data: [{1.2, 3.4}, {2.1, 4.5}, {1.8, 3.9}], color: :cyan},
    %{name: "Cluster B", data: [{5.0, 1.2}, {4.8, 1.5}, {5.3, 0.9}], color: :yellow}
  ],
  width: 50,
  height: 15,
  show_axes: true,
  x_range: {0, 7},
  y_range: {0, 6}
)

heatmap

2D grid with color intensity.

heatmap(
  data: [
    [0.1, 0.4, 0.9, 0.6],
    [0.3, 0.8, 0.5, 0.2],
    [0.7, 0.2, 0.3, 0.8]
  ],
  width: 40,
  height: 6,
  color_scale: :warm,
  show_values: true
)

Color scales: :warm (yellow -> red), :cool (cyan -> blue), :diverging (blue -> white -> red), or a custom fn(value, min, max) -> {r, g, b}.


Advanced

process_component

Run any component in its own supervised process for crash isolation. If it crashes, it restarts automatically without affecting the rest of the app.

# In your view:
process_component(MyExpensiveWidget, %{path: "/var/log"})

focus_ring

Visual focus indicator for accessibility. Highlights the currently focused component.

Component module: Raxol.UI.Components.FocusRing

Options: color, width, offset, style (:solid), components (list of component IDs to track).


Using Components Directly

The View DSL functions cover most needs. When you need full control -- handling events, managing component state, accessing all options -- use the component modules directly:

alias Raxol.UI.Components.Input.TextInput

# Initialize with full options
{:ok, state} = TextInput.init(%{
  id: "search",
  value: "",
  placeholder: "Search...",
  max_length: 100,
  on_submit: :do_search
})

# Handle an event
state = TextInput.handle_event(event, state, context)

# Render
rendered = TextInput.render(state, context)

All component modules follow the same pattern: init/1 -> handle_event/3 -> render/2.


Quick Reference

WidgetDSL functionComponent moduleInteractive?
columncolumn do--No
rowrow do--No
boxbox do--No
spacerspacer/1--No
dividerdivider/1--No
split_panesplit_pane/1UI.Layout.SplitPaneNo
texttext/1--No
labellabel/1--No
listlist/1--No
progressprogress/1Display.ProgressNo
tabletable/1Display.TableYes
tree--Display.TreeYes
viewport--Display.ViewportYes
status_bar--Display.StatusBarNo
code_block--CodeBlockNo
markdown--MarkdownRendererNo
imageimage/1--No
buttonbutton/1Input.ButtonYes
text_inputtext_input/1Input.TextInputYes
textareatextarea/1Input.MultiLineInputYes
checkboxcheckbox/1Input.CheckboxYes
radio_groupradio_group/1--Yes
selectselect/1Input.SelectListYes
tabstabs/1Input.TabsYes
menu--Input.MenuYes
modalmodal/1ModalYes
sparklinesparkline/1--No
line_chartline_chart/1--No
bar_chartbar_chart/1--No
scatter_chartscatter_chart/1--No
heatmapheatmap/1--No
spinner--Progress.SpinnerNo
focus_ring--FocusRingNo

All component module paths are under Raxol.UI.Components.*.


Running Examples

# Interactive playground with all demos
mix raxol.playground

# Flagship demo (dashboard, sparklines, live stats)
mix run examples/demo.exs

# Simple starting point
mix run examples/getting_started/counter.exs

# Full widget showcase
mix run examples/apps/showcase_app.exs