Skip to content
Subha Sundar Das edited this page Jul 14, 2026 · 2 revisions

RetUI includes a growing collection of ready-made components such as buttons, text inputs, checkboxes, lists, trees, and panels. These components help you build terminal applications quickly without creating every widget from scratch.

This guide explains how to use the built-in components and how to create your own.


The Builder Pattern

Most interactive components use the same builder pattern.

components.SomeComponent().
    ID("unique-id").
    // Configure the component...
    OnChange(func(id string, value T) {
        // Handle changes
    }).
    Render()

The pattern is always the same:

  1. Create the component with its constructor.
  2. Configure it using chained methods.
  3. Give it a unique ID.
  4. Finish with .Render().

Without calling .Render(), the builder never becomes a retui.Element.


Button

Buttons respond to keyboard interaction when focused.

components.Button().
    ID("submit").
    Label("Submit").
    Style(retui.NewStyle().Background(retui.Blue)).
    HoverStyle(retui.NewStyle().Background(retui.Cyan)).
    OnClick(func(id string) {
        retui.Debug("Button clicked:", id)
    }).
    Render()

Common Methods

  • ID()
  • Label()
  • Style()
  • HoverStyle()
  • ActiveStyle()
  • Prefix()
  • Suffix()
  • Focused()
  • OnClick()

TextInput

A single-line editable text field.

name, setName := retui.UseState("")

components.TextInput().
    ID("name").
    Value(name).
    Placeholder("Enter your name").
    Focused(retui.IsFocused("name")).
    OnChange(func(id string, value string) {
        setName(value)
    }).
    Render()

Common Methods

  • Value()
  • Placeholder()
  • MinLength()
  • MaxLength()
  • Prefix()
  • Suffix()
  • Focused()
  • OnChange()
  • OnSubmit()

Controlled Component

RetUI components are controlled components.

Your application owns the value.

Value(name)

Whenever the user edits the field:

OnChange(func(id string, value string) {
    setName(value)
})

Your state changes, and RetUI automatically refreshes the screen.


Password

A password input behaves like a text input but hides typed characters.

components.Password().
    ID("password").
    Value(password).
    MaskChar("•").
    ShowLastChar(true).
    OnChange(func(id string, value string) {
        setPassword(value)
    }).
    Render()

Common Methods

  • Value()
  • MaskChar()
  • ShowLastChar()
  • OnChange()

NumberInput

Accepts only numeric input.

price, setPrice := retui.UseState(0.0)

components.NumberInput().
    ID("price").
    Value(price).
    Min(0).
    Max(1000).
    Step(0.5).
    Decimals(2).
    OnChange(func(id string, value float64) {
        setPrice(value)
    }).
    Render()

Common Methods

  • Value()
  • Min()
  • Max()
  • Step()
  • Decimals()
  • ArrowStep()
  • OnChange()

When ArrowStep(true) is enabled, the Up and Down arrow keys increase or decrease the value by the configured step.


DateInput

A formatted date input with validation.

components.DateInput().
    ID("dob").
    Value(dob).
    Format("DD/MM/YYYY").
    Min("01/01/1900").
    Max("31/12/2026").
    OnChange(func(id string, value string) {
        setDob(value)
    }).
    Render()

Common Methods

  • Value()
  • Format()
  • Min()
  • Max()
  • OnChange()

Checkbox

A simple on/off option.

components.Checkbox().
    ID("agree").
    Checked(agree).
    Label("I agree to the terms").
    OnChange(func(id string, checked bool) {
        setAgree(checked)
    }).
    Render()

Common Methods

  • Checked()
  • Label()
  • OnChange()

SelectPicker

Choose one option from a list.

components.SelectPicker().
    ID("country").
    Options([]string{
        "USA",
        "India",
        "UK",
    }).
    Selected(selectedIndex).
    OnChange(func(id string, index int, value string) {
        setSelectedIndex(index)
    }).
    Render()

Common Methods

  • Options()
  • Selected()
  • OnChange()
  • OnSubmit()

OnChange() is called while moving through the options.

OnSubmit() is called when the selection is confirmed with Enter.


List

A scrollable list of selectable items.

components.List().
    ID("results").
    Items([]string{
        "Apple",
        "Banana",
        "Cherry",
    }).
    Selected(selectedIndex).
    OnSelect(func(id string, index int, value string) {
        setSelectedIndex(index)
    }).
    Render()

Common Methods

  • Items()
  • Selected()
  • OnSelect()

Tree

Unlike other components, Tree is not a builder.

It is a regular function because tree structures are naturally hierarchical.

var fileTree = []components.TreeNode{
    {
        ID:    "src",
        Label: "src",
        Children: []components.TreeNode{
            {
                ID:    "main.go",
                Label: "main.go",
            },
            {
                ID:    "utils.go",
                Label: "utils.go",
            },
        },
    },
}

components.Tree(
    "file-tree",
    fileTree,
    retui.IsFocused("file-tree"),
    func(id string) {
        retui.Debug("Selected:", id)
    },
)

Tree expansion and collapse are managed internally using UseStateKeyed().

You only provide the tree data and selection callback.


Panel

A bordered container with a title.

components.Panel(
    "User Details",
    50,
    retui.Text("Name: Subha", retui.NewStyle()),
    retui.Text("Role: Admin", retui.NewStyle()),
)

Unlike builder components, Panel() immediately returns a retui.Element.

No .Render() call is needed.

Use retui.Box() when you don't need a border or title.


Building Your Own Component

Every built-in component is simply a Go function returning a retui.Element.

You can build your own exactly the same way.

Simple Component

func UserCard(name string, role string) retui.Element {
    return retui.Box(
        retui.Props{
            Direction: retui.Column,
            Padding: [4]int{1, 2, 1, 2},
        },
        retui.NewStyle().Border(
            retui.Border{
                Top:    true,
                Right:  true,
                Bottom: true,
                Left:   true,
            },
        ),

        retui.Text(
            name,
            retui.NewStyle().Bold(true),
        ),

        retui.Text(
            role,
            retui.NewStyle().
                Foreground(retui.Hex("#888888")),
        ),
    )
}

Use it like any built-in component.

retui.Box(
    retui.Props{
        Direction: retui.Row,
        Gap:       2,
    },
    retui.NewStyle(),

    UserCard("Subha", "Admin"),
    UserCard("Alex", "Editor"),
)

Stateful Component

Custom components can use hooks just like the built-in ones.

func Counter() retui.Element {
    count, setCount := retui.UseState(0)

    key, active := retui.UseFocusedKeySimple(
        retui.IsFocused("counter"),
    )

    if active {
        switch key.Code {
        case retui.KeyUp:
            setCount(count + 1)

        case retui.KeyDown:
            setCount(count - 1)
        }
    }

    return retui.Box(
        retui.Props{
            Direction: retui.Column,
        },
        retui.NewStyle(),

        retui.Text(
            fmt.Sprintf("Counter: %d", count),
            retui.NewStyle(),
        ),
    )
}

This example uses:

  • UseState()
  • Focus management
  • Keyboard events
  • Box
  • Text

These are the same building blocks used by RetUI's own components.


Component Summary

Component Constructor Main Methods
Button components.Button() Label(), OnClick()
TextInput components.TextInput() Value(), OnChange(), OnSubmit()
Password components.Password() Value(), MaskChar(), OnChange()
NumberInput components.NumberInput() Value(), Min(), Max(), Step(), OnChange()
DateInput components.DateInput() Value(), Format(), Min(), Max(), OnChange()
Checkbox components.Checkbox() Checked(), Label(), OnChange()
SelectPicker components.SelectPicker() Options(), Selected(), OnChange()
List components.List() Items(), Selected(), OnSelect()
Tree components.Tree() Function-based API
Panel components.Panel() Function-based API

Remember

Most interactive components follow this pattern:

components.Component().
    ID("id").
    // Configure...
    Render()

Only Tree and Panel are different—they are regular functions that return a retui.Element directly and do not require .Render().

Clone this wiki locally