Charm Bracelet
Examples

Sequence

What This Example Shows

This example demonstrates how to use tea.Sequence to execute commands in a strict order, ensuring each completes before the next starts. It combines tea.Sequence with tea.Batch for concurrent sub-tasks within the sequence.

The key here is tea.Sequence, which runs commands sequentially, waiting for each message to be processed before proceeding—ideal for ordered operations like initialization or animations.

Understanding Sequential Commands in Bubble Tea

tea.Sequence runs a series of commands one after another, only starting the next after the previous command's message has been handled in Update. This contrasts with tea.Batch, which starts all commands concurrently without order guarantees.

This is useful for scenarios requiring step-by-step execution, preventing overlap in tasks like printing messages or performing setup.

This approach is perfect for creating predictable flows in your app, such as phased loading or sequenced outputs, while still allowing concurrency where needed via nested batches.

Here's what happens:

  • The sequence starts with a batch printing "A", "B", "C" concurrently
  • After the batch completes, it prints "Z"
  • Finally, it quits the program

How It Works

Set Up the Sequence

In Init, return a sequence of commands:

return tea.Sequence(
    tea.Batch(
        tea.Println("A"),
        tea.Println("B"),
        tea.Println("C"),
    ),
    tea.Println("Z"),
    tea.Quit,
)

Batch runs concurrently; sequence ensures order.

Handle Key Presses

In Update, quit on any key for manual exit:

switch msg.(type) {
case tea.KeyMsg:
    return m, tea.Quit
}

Though the sequence handles auto-quit.

Minimal Rendering

View returns empty as output is via Println:

func (m model) View() string {
    return ""
}

All display is command-driven.

Sequence relies on messages being processed; complex commands might need careful handling to avoid unexpected delays.

Code Breakdown

Empty model as state isn't needed:

type model struct{}

Simple struct for minimal app.

Initialize and run:

func main() {
    if _, err := tea.NewProgram(model{}).Run(); err != nil {
        fmt.Println("Uh oh:", err)
        os.Exit(1)
    }
}

func (m model) Init() tea.Cmd {
    // Sequence here
}

Run handles execution.

Core init logic:

func (m model) Init() tea.Cmd {
    return tea.Sequence(
        tea.Batch(
            tea.Println("A"),
            tea.Println("B"),
            tea.Println("C"),
        ),
        tea.Println("Z"),
        tea.Quit,
    )
}

Ordered execution with concurrent sub-group.

Minimal update:

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg.(type) {
    case tea.KeyMsg:
        return m, tea.Quit
    }
    return m, nil
}

Only for manual quit.

The View

No persistent UI; returns empty string:

func (m model) View() string {
    return ""
}

Output comes from tea.Println commands.

sequence example demonstration

Final Code

main.go
package main

import (
	"fmt"
	"os"

	tea "github.com/charmbracelet/bubbletea"
)

type model struct{}

func (m model) Init() tea.Cmd {
	return tea.Sequence(
		tea.Batch(
			tea.Println("A"),
			tea.Println("B"),
			tea.Println("C"),
		),
		tea.Println("Z"),
		tea.Quit,
	)
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg.(type) {
	case tea.KeyMsg:
		return m, tea.Quit
	}
	return m, nil
}

func (m model) View() string {
	return ""
}

func main() {
	if _, err := tea.NewProgram(model{}).Run(); err != nil {
		fmt.Println("Uh oh:", err)
		os.Exit(1)
	}
}

How is this guide?