Component Gallery

View Source

All Components 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 Components 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: :none, :single, :double, :rounded, :bold, :dashed.

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

Components for showing information to the user. These are all display-only, with no user interaction.

text

Styled text content. The most basic Component.

# 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})

# fg/bg also work as keyword opts
text("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.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

Components 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

Components 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("Loading...", 0, type: :dots)
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(MyExpensiveComponent, %{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

ComponentDSL functionModule pathInteractive?
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/1TableYes
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 Component showcase
mix run examples/apps/showcase_app.exs