---
title: Go CLI with Bubble Tea
description: Create a Go terminal application with Bubble Tea using Better Fullstack's Go CLI project scaffolding.
updated: 2026-05-12
category: Go
tags:
  - go
  - cli
  - bubbletea
keywords:
  - go bubble tea starter
  - go cli boilerplate
  - create go cli
  - bubbletea terminal app
  - go tui starter
---

Use this stack when you want a Go terminal UI instead of a web API.

```npm
npm create better-fullstack@latest my-go-cli -- \
  --ecosystem go \
  --go-web-framework none \
  --go-orm none \
  --go-api none \
  --go-cli bubbletea \
  --go-logging none \
  --go-auth none
```

## What this creates

- A Go CLI project.
- Bubble Tea as the terminal UI option.
- No web framework, ORM, gRPC, or auth add-on.

## Generated shape

This stack creates an interactive terminal app, not a server. Bubble Tea organizes the program around a model, messages, updates, and views.

The main loop is simple:

- The model holds current application state.
- Messages describe input, ticks, or command results.
- `Update` changes the model and returns follow-up commands.
- `View` renders the current model as terminal text.

## Representative file tree

```txt
my-go-cli/
  go.mod
  cmd/
    app/
      main.go
  internal/
    tui/
      model.go
      update.go
      view.go
```

Keep side effects in Bubble Tea commands where possible. That makes the model and view easier to test without running a real terminal.

## Model and update examples

A Bubble Tea model can start with just enough state to render and react to input:

```go
package tui

type Model struct {
	Items    []string
	Selected int
	Quitting bool
}

func NewModel() Model {
	return Model{
		Items: []string{"Build", "Test", "Deploy"},
	}
}
```

The update function owns state transitions:

```go
package tui

import tea "github.com/charmbracelet/bubbletea"

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.String() {
		case "q", "ctrl+c":
			m.Quitting = true
			return m, tea.Quit
		case "j", "down":
			if len(m.Items) > 0 {
				m.Selected = (m.Selected + 1) % len(m.Items)
			}
		case "k", "up":
			if len(m.Items) > 0 {
				m.Selected = (m.Selected - 1 + len(m.Items)) % len(m.Items)
			}
		}
	}

	return m, nil
}
```

The view should render from state without performing work:

```go
package tui

import "strings"

func (m Model) View() string {
	if m.Quitting {
		return ""
	}

	var b strings.Builder
	b.WriteString("Tasks\n\n")

	for i, item := range m.Items {
		cursor := " "
		if i == m.Selected {
			cursor = ">"
		}
		b.WriteString(cursor + " " + item + "\n")
	}

	b.WriteString("\nq: quit  j/k: move\n")
	return b.String()
}
```

## Compatibility notes

CLI projects should keep server-only categories set to `none` unless they are intentionally hybrid tools.

Keep `--go-cli bubbletea` with `--go-web-framework none`, `--go-orm none`, and `--go-api none` for a terminal-first app. If the CLI later talks to an API, keep that client in a separate package and call it through commands instead of from `View`.

## When to choose it

Choose Bubble Tea for terminal dashboards, developer tools, and command-line interfaces where interactivity matters.

It is a good fit for tools that need selection, progress, forms, or live status without building a browser UI.

## Testing and deployment notes

Unit test the model transitions directly:

```bash
go test ./...
```

For distribution, build a single binary and document supported environment variables or config files:

```bash
go build ./cmd/app
```

If the generated project uses a different command path, follow the path in its `cmd/` directory.

## Troubleshooting

- If key presses do nothing, verify the focused terminal is sending the key sequence Bubble Tea receives.
- If the screen leaves behind stale output, check program startup and shutdown options for alternate-screen behavior.
- If tests are hard to write, move side effects into `tea.Cmd` functions and keep `Update` deterministic.
- If output wraps poorly, test at narrow widths and keep `View` formatting responsive to available space when the model tracks dimensions.

## Next steps

- Open the [Stack Builder](/new?eco=go&gwf=none&gorm=none&gapi=none&gcli=bubbletea&glog=none&gauth=none).
- Read the [Go ecosystem docs](/docs/ecosystems/#go).
