Components
Write a function that returns *ui.Element. Start with View, Text, and Button, then compose the rest of the control set the same way.
#Defining components
Pass ordinary Go values. If an argument reads a signal, that binding stays live after the function returns — you do not need extra annotations.
func CounterLabel(count int) *ui.Element {
return ui.Text("Count: ", count).FontSize(20)
}
func ComponentExample() *ui.Element {
count, setCount := ui.CreateSignal(0)
return ui.View().Children(
CounterLabel(count()),
ui.Button().Child("Increment").OnClick(func() { setCount(count() + 1) }),
)
}Structs work the same way. Updating quantity changes the text; LineItem stays mounted.
type Product struct {
ID string
Name string
Price int
}
func LineItem(product Product, quantity int) *ui.Element {
return ui.Text(product.Name, " × ", quantity, " = $", product.Price*quantity)
}
func Cart() *ui.Element {
quantity, setQuantity := ui.CreateSignal(1)
return ui.View().Children(
LineItem(Product{ID: "mug", Name: "Mug", Price: 12}, quantity()),
ui.Button().Child("Add one").OnClick(func() { setQuantity(quantity() + 1) }),
)
}Start containers with ui.View() and append content with .Child(...) or .Children(...).
func CardComponent(title string, children ...any) *ui.Element {
return ui.View().FlexCol().Padding(20).Gap(12).Children(
ui.Text(title).FontWeight(600),
children,
)
}#Primitives
View, Text, Button, and Input cover most layout and text. Chain styles, properties, and handlers on the same element.
The optional extensions/editor package adds the Rust-core Editor, CodeBlock, and DiffView surfaces without CGO or another native library.
The optional extensions/markdown package adds retained Markdown, including streaming reconciliation and CodeBlock rendering.
func ButtonExample(save func()) *ui.Element {
return ui.Button().Child("Save changes").
Height(40).
PaddingLeft(16).
PaddingRight(16).
BackgroundColor("#2563eb").
TextColor("white").
BorderRadius(8).
Hover(func(s ui.StyleBuilder) ui.StyleBuilder { return s.BackgroundColor("#3b82f6") }).
OnClick(save)
}#Compound components
Create an instance, then compose its named parts. Strings work as children.
func TabsExample() *ui.Element {
tabs := ui.NewTabs().DefaultValue("account")
return tabs.Root().Children(
tabs.List().Children(
tabs.Tab("account").Child("Account"),
tabs.Tab("security").Child("Security"),
),
tabs.Panel("account").Child("Account settings"),
tabs.Panel("security").Child("Security settings"),
)
}Configure behavior on the instance and styles on its parts. Nest parts the way you nest any other children.
#Controlled state
Pass the current value and write the next value from the change callback. Create each instance inside the component that owns it.
func CheckboxExample() *ui.Element {
checked, setChecked := ui.CreateSignal(false)
checkbox := ui.NewCheckbox().Checked(checked()).OnCheckedChange(
func(next bool, _ *native.Event) { setChecked(next) },
)
return checkbox.Root().FlexRow().Gap(8).Children(
checkbox.Indicator().Child("✓"),
"Remember me",
)
}#Component families
The component reference covers forms, overlays, menus, tables, trees, and more. Run the gallery from this repository:
bun packages/cli/src/cli.ts dev --project examples/components#Styling parts
Apply styles on the part itself, or share a ui.Style() value:
triggerStyle := ui.Style().Padding(12).RoundedLg().Bg("#2563eb")
return popover.Trigger().Style(triggerStyle).TextColor("#ffffff").Child("Account")Adding OnClick keeps the part’s default action. Call PreventDefault() on OnClickEvent when you need to cancel it.
