3.6 KiB
Bubble Tea Design Patterns
Common architectural patterns for TUI development.
Pattern 1: Single-View Application
When: Simple, focused TUIs with one main view Components: 1-3 components, single model struct Complexity: Low
type model struct {
mainComponent component.Model
ready bool
}
Pattern 2: Multi-View State Machine
When: Multiple distinct screens (setup, main, done) Components: State enum + view-specific components Complexity: Medium
type view int
const (
setupView view = iota
mainView
doneView
)
type model struct {
currentView view
// Components for each view
}
Pattern 3: Composable Views
When: Complex UIs with reusable sub-components Pattern: Embed multiple bubble models Example: Dashboard with multiple panels
type model struct {
panel1 Panel1Model
panel2 Panel2Model
panel3 Panel3Model
}
// Each panel is itself a Bubble Tea model
Pattern 4: Master-Detail
When: Selection in one pane affects display in another Example: File list + preview, Email list + content Layout: Two-pane or three-pane
type model struct {
list list.Model
detail viewport.Model
selectedItem int
}
Pattern 5: Form Flow
When: Multi-step data collection Pattern: Array of inputs + focus management Example: Configuration wizard
type model struct {
inputs []textinput.Model
focusIndex int
step int
}
Pattern 6: Progress Tracker
When: Long-running sequential operations Pattern: Queue + progress per item Example: Installation, download manager
type model struct {
items []Item
currentIndex int
progress progress.Model
spinner spinner.Model
}
Layout Patterns
Vertical Stack
lipgloss.JoinVertical(lipgloss.Left,
header,
content,
footer,
)
Horizontal Panels
lipgloss.JoinHorizontal(lipgloss.Top,
leftPanel,
separator,
rightPanel,
)
Three-Column (File Manager Style)
lipgloss.JoinHorizontal(lipgloss.Top,
parentDir, // 25% width
currentDir, // 35% width
preview, // 40% width
)
Message Passing Patterns
Custom Messages
type myCustomMsg struct {
data string
}
func doSomethingCmd() tea.Msg {
return myCustomMsg{data: "result"}
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case myCustomMsg:
// Handle custom message
}
}
Async Operations
func fetchDataCmd() tea.Cmd {
return func() tea.Msg {
// Do async work
data := fetchFromAPI()
return dataFetchedMsg{data}
}
}
Error Handling Pattern
type errMsg struct{ err error }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case errMsg:
m.err = msg.err
m.errVisible = true
return m, nil
}
}
Keyboard Navigation Pattern
case tea.KeyMsg:
switch msg.String() {
case "up", "k":
m.cursor--
case "down", "j":
m.cursor++
case "enter":
m.selectCurrent()
case "q", "ctrl+c":
return m, tea.Quit
}
Responsive Layout Pattern
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
// Update component dimensions
m.viewport.Width = msg.Width
m.viewport.Height = msg.Height - 5 // Reserve space for header/footer
Help Overlay Pattern
type model struct {
showHelp bool
help help.Model
}
func (m model) View() string {
if m.showHelp {
return m.help.View()
}
return m.mainView()
}