-
Notifications
You must be signed in to change notification settings - Fork 4
Core Concept
Before learning individual components, it helps to understand how RetUI works under the hood.
Good news: there are only a handful of ideas to learn, and they all build on each other.
In RetUI, your application is built by combining small components.
A component is simply a Go function that returns a retui.Element.
func Greeting() retui.Element {
return retui.Text("Hello, World!", retui.NewStyle())
}
You can use this component anywhere in your application:
func App() retui.Element {
return Greeting()
}
Think of components as building blocks. Instead of writing one giant function, you build many small, reusable pieces — then combine them.
Components can contain other components. Box is the primary layout primitive in RetUI — it's a flexbox-style container that arranges its children, and every layout (rows, columns, panels, forms) is built from it.
func App() retui.Element {
return retui.Box(
retui.Props{Direction: retui.Column},
retui.NewStyle(),
retui.Text("Welcome", retui.NewStyle().Bold(true)),
retui.Text("Hello RetUI!", retui.NewStyle()),
)
}
You can keep nesting boxes to build more complex interfaces:
Box (Column)
├── Text ("Welcome")
└── Text ("Hello RetUI!")
Real apps might look more like this:
Box ("Dashboard", Column)
├── Box (Row)
│ ├── UserPanel
│ └── StatsPanel
└── Footer
Whenever your application's state changes, RetUI rebuilds the UI.
Don't worry — this doesn't mean the entire terminal redraws. RetUI compares the old UI with the new one and updates only the parts that changed.
Example: if only a counter changes —
Before:
Counter: 5
After clicking a button:
Counter: 6
Only the number is updated. Everything else on screen stays untouched. This keeps applications fast and responsive, even as they grow.
State is simply data that can change over time.
Without state, text is frozen forever:
retui.Text("Counter: 0", retui.NewStyle())
With state, it can update:
count, setCount := retui.UseState(0)
retui.Text(fmt.Sprintf("Counter: %d", count), retui.NewStyle())
Later, update it from anywhere — like a button handler:
setCount(count + 1)
RetUI automatically refreshes the screen to reflect the new value.
Full example — a working counter:
import (
"fmt"
"github.com/subhasundardass/retui/retui"
"github.com/subhasundardass/retui/retui/components"
)
func Counter() retui.Element {
count, setCount := retui.UseState(0)
return retui.Box(
retui.Props{Direction: retui.Column, Gap: 1},
retui.NewStyle(),
retui.Text(fmt.Sprintf("Counter: %d", count), retui.NewStyle()),
components.Button().
ID("increment").
Label("Increment").
OnClick(func(id string) {
setCount(count + 1)
}).
Render(),
)
}
Hooks let a component remember information between renders — like state or side effects.
The hooks available today are:
| Hook | Purpose |
|---|---|
| UseState | Store a value that can change, keyed by render position |
| UseStateKeyed | Like UseState, but keyed by a stable string — use when the number of hook calls can vary between renders (e.g. expandable tree nodes) |
| UseEffect | Run code in response to changes (e.g. on mount, on dependency change), with optional cleanup |
| UseContext / CreateContext | Share a value down the component tree without passing it manually through every layer |
Don't worry if these are new — each hook has its own dedicated guide later in the documentation. For now, just know: hooks are how components remember things.
Components don't decide where they appear on screen — the layout system does, via Box and its Direction prop.
retui.Row — places children side by side:
retui.Box(
retui.Props{Direction: retui.Row, Gap: 2},
retui.NewStyle(),
retui.Text("Left", retui.NewStyle()),
retui.Text("Right", retui.NewStyle()),
)
Left Right
retui.Column — stacks children vertically:
retui.Box(
retui.Props{Direction: retui.Column},
retui.NewStyle(),
retui.Text("One", retui.NewStyle()),
retui.Text("Two", retui.NewStyle()),
retui.Text("Three", retui.NewStyle()),
)
One
Two
Three
You can also nest boxes — a Row-direction Box containing Column-direction boxes, or vice versa — to build complex screens without calculating positions yourself. Props also supports Gap, Padding, Align, Justify, and Width/Height sizing (Fixed, Grow, Fit) for finer control.
Terminal applications are usually controlled entirely by keyboard. RetUI keeps track of which component is currently focused, so navigation "just works."
Example — a form:
Name: [Subha ]
Email: [ ]
Pressing Tab moves focus to the next field:
Name: [Subha ]
Email: [user@mail.com]
RetUI exposes this through functions like retui.SetFocus, retui.CurrentFocus, retui.IsFocused, and retui.SetFocusOrder — most components handle focus styling automatically once you tell RetUI the order fields should follow.
Once you build a component, you can reuse it anywhere in your app.
Instead of repeating this every time you need a labeled section:
retui.Box(
retui.Props{Direction: retui.Column},
retui.NewStyle().Bold(true),
retui.Text("User", retui.NewStyle().Bold(true)),
)
Wrap it in a function once:
func UserPanel() retui.Element {
return retui.Box(
retui.Props{Direction: retui.Column},
retui.NewStyle(),
retui.Text("User", retui.NewStyle().Bold(true)),
)
}
And reuse it wherever you need it:
func App() retui.Element {
return retui.Box(
retui.Props{Direction: retui.Column, Gap: 1},
retui.NewStyle(),
UserPanel(),
UserPanel(),
)
}
Reusable components keep applications easier to build, test, and maintain as they grow.
You only need to remember a few ideas to start building with RetUI:
-
Everything is a component — a function that returns an
Element. -
Boxis the core layout primitive — components nest inside boxes, and boxes nest inside boxes. - State changes automatically update the screen — no manual redraw logic.
-
Hooks (
UseState,UseStateKeyed,UseEffect,UseContext) manage state and behavior. -
Direction: retui.Roworretui.Columnon aBoxcontrols where children appear. -
Focus is tracked by RetUI and driven by functions like
SetFocusandSetFocusOrder. - Small, reusable components are easier to maintain than one large function.
That's it! Once these click, the rest of the RetUI documentation will feel much more natural — everything else is just more components, more hooks, and more Box layouts built on top of these same ideas.