The heart of your Bubble Tea application - managing state and defining behavior
The Model is the foundation of every Bubble Tea application. It holds your application’s state and defines three core methods that control how your program behaves.
// Model contains the program's state as well as its core functions.type Model interface { // Init is the first function that will be called. It returns an optional // initial command. To not perform an initial command return nil. Init() Cmd // Update is called when a message is received. Use it to inspect messages // and, in response, update the model and/or send a command. Update(Msg) (Model, Cmd) // View renders the program's UI, which can be a string or a [Layer]. The // view is rendered after every Update. View() View}
Any type that implements these three methods can be a Model. While structs are most common, you could even use a simple type like int or string for very basic applications.
Never mutate the model in place and return it!While Go allows this, it’s better to treat Update as if it returns a new model. This makes your code more predictable.
// This works but is not recommendedfunc (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.count++ // Mutating in place return m, nil}// Better: be explicit about what changedfunc (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.count = m.count + 1 // Clear that we're updating return m, nil}
type model struct { textInput textinput.Model list list.Model}func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd // Let the text input handle its messages var cmd tea.Cmd m.textInput, cmd = m.textInput.Update(msg) cmds = append(cmds, cmd) // Let the list handle its messages m.list, cmd = m.list.Update(msg) cmds = append(cmds, cmd) // Handle your own messages switch msg := msg.(type) { case tea.KeyPressMsg: if msg.String() == "enter" { return m, m.submit() } } return m, tea.Batch(cmds...)}
func (m model) View() tea.View { s := "What should we buy at the market?\n\n" for i, choice := range m.choices { cursor := " " if m.cursor == i { cursor = ">" } checked := " " if _, ok := m.selected[i]; ok { checked = "x" } s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice) } s += "\nPress q to quit.\n" return tea.NewView(s)}