Core Concepts

Commands

Learn how to perform I/O and async operations in Bubble Tea.

Commands are how you perform I/O in Bubble Tea. They're functions that return a tea.Msg and run asynchronously.

What is a Command?

A tea.Cmd is a function that performs I/O and returns a tea.Msg:

type Cmd func() Msg

Commands are never run directly by you. Instead, you return them from Init or Update, and the Bubble Tea runtime executes them.

Simple Commands

Here's a command that makes an HTTP request:

func checkServer() tea.Msg {
    res, err := http.Get("https://api.example.com/status")
    if err != nil {
        return errMsg{err}
    }
    defer res.Body.Close()
    return statusMsg(res.StatusCode)
}

Return it from Update:

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyPressMsg:
        if msg.String() == "c" {
            return m, checkServer // Run the command
        }
    case statusMsg:
        m.status = int(msg)
        return m, nil
    case errMsg:
        m.err = msg.err
        return m, nil
    }
    return m, nil
}

Built-in Commands

Bubble Tea provides several useful commands:

Timed Commands

Use tea.Tick for periodic updates:

type tickMsg time.Time

func tickEvery() tea.Cmd {
    return tea.Tick(time.Second, func(t time.Time) tea.Msg {
        return tickMsg(t)
    })
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg.(type) {
    case tickMsg:
        m.time = time.Now()
        return m, tickEvery() // Schedule next tick
    }
    return m, nil
}

Command Factories

Create commands that take parameters:

func fetchUser(id int) tea.Cmd {
    return func() tea.Msg {
        user, err := api.GetUser(id)
        if err != nil {
            return errMsg{err}
        }
        return userMsg{user}
    }
}

// Use it:
return m, fetchUser(42)
Commands are the key to keeping your Update function pure. All side effects go through commands!

Common Patterns

Loading States

type model struct {
    loading bool
    data    []string
    err     error
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyPressMsg:
        if msg.String() == "r" {
            m.loading = true
            return m, fetchData
        }
    case dataMsg:
        m.loading = false
        m.data = msg.data
    case errMsg:
        m.loading = false
        m.err = msg.err
    }
    return m, nil
}

Debouncing

type debounceMsg struct{}

func debounce(d time.Duration) tea.Cmd {
    return tea.Tick(d, func(time.Time) tea.Msg {
        return debounceMsg{}
    })
}