Skip to content

Releases: charmbracelet/bubbletea

v2.0.0-rc.2

17 Nov 20:53
v2.0.0-rc.2
15f884b

Choose a tag to compare

v2.0.0-rc.2 Pre-release
Pre-release

Smooth Operator (Mode 2026)

This release introduces synchronized output updates, which is enabled by default. This mode enhances how supported terminals render program updates by triggering the updates atomically.

We've also fixed a bunch of renderer bugs and edge cases ๐Ÿ™‚

Changelog

New!

Fixed

Docs

Other stuff


The Charm logo

Thoughts? Questions? We love hearing from you. Feel free to reach out on X, Discord, Slack, The Fediverse, Bluesky.

v2.0.0-rc.1

04 Nov 20:08
v2.0.0-rc.1
cc0168c

Choose a tag to compare

v2.0.0-rc.1 Pre-release
Pre-release

This release includes a big change in the module name, and several message type changes. These types changed from type aliases to structs to improve extensibility and allow for future enhancements without breaking changes:

  • github.com/charmbracelet/bubbletea/v2 - now moved to charm.land/bubbletea/v2
  • CursorPositionMsg - now a struct type
  • KeyboardEnhancementsMsg - now a struct type
  • PasteMsg - now a struct type
  • CapabilityMsg - now a struct type
  • TerminalVersionMsg - now struct types

Migration Guide

Charm Land!

// Before
import tea "github.com/charmbracelet/bubbletea/v2"

// After
import tea "charm.land/bubbletea/v2"

Or you can use this GNU sed oneliner in your project root ๐Ÿ˜‰

find . -name \*.go | xargs -I{} sed -i 's/"github.com\/charmbracelet\/bubbletea\/v2"/"charm.land\/bubbletea\/v2"/' {}

CursorPositionMsg

// Before
case CursorPositionMsg:
    x, y := msg.X, msg.Y

// After (no change needed - fields remain the same)
case CursorPositionMsg:
    x, y := msg.X, msg.Y

KeyboardEnhancementsMsg

// Before
case KeyboardEnhancementsMsg:
    if msg&ansi.KittyDisambiguateEscapeCodes != 0 {
        // ...
    }

// After
case KeyboardEnhancementsMsg:
    if msg.Flags&ansi.KittyDisambiguateEscapeCodes != 0 {
        // ...
    }
    // Or use the helper methods:
    if msg.SupportsKeyDisambiguation() {
        // ...
    }

PasteMsg

// Before
case PasteMsg:
    content := string(msg)

// After
case PasteMsg:
    content := msg.Content
    // Or use the String() method:
    content := msg.String()

CapabilityMsg

// Before
case CapabilityMsg:
    switch msg {
    case "RGB", "Tc":
        // ...
    }

// After
case CapabilityMsg:
    switch msg.Content {
    case "RGB", "Tc":
        // ...
    }
    // Or use the String() method:
    switch msg.String() {
    case "RGB", "Tc":
        // ...
    }

TerminalVersionMsg

// Before
case TerminalVersionMsg:
    version := string(msg)

// After
case TerminalVersionMsg:
    version := msg.Name
    // Or use the String() method:
    version := msg.String()

Changelog

Docs

Other stuff


The Charm logo

Thoughts? Questions? We love hearing from you. Feel free to reach out on X, Discord, Slack, The Fediverse, Bluesky.

v2.0.0-beta.6

30 Oct 18:39
v2.0.0-beta.6

Choose a tag to compare

v2.0.0-beta.6 Pre-release
Pre-release

Renderer Fixes

This new beta release includes a number of fixes related to the renderer.

Changelog

Fixed


The Charm logo

Thoughts? Questions? We love hearing from you. Feel free to reach out on X, Discord, Slack, The Fediverse, Bluesky.

v2.0.0-beta.5

22 Oct 20:13
v2.0.0-beta.5
b4d105e

Choose a tag to compare

v2.0.0-beta.5 Pre-release
Pre-release

Bubble Tea beta 5 is here!

We're excited to share the new Bubble Tea v2 beta 5 release with you! In this release, we focused on refining the View API, making it more declarative with a single source of truth for view-related properties.

Summary

All the changes since Beta 4 are to supporting a more declarative View type. This might sound like a lot but it's fairly simple in practice. This is what it looks like now:

func (m model) View() tea.View {
    v := tea.NewView("Hi, mom!")
    v.AltScreen = true
    v.WindowTitle = "Itโ€™s all about Mom today"
}

Why the change?

The general impetus for doing this is to eliminate the possibility of race conditions and greatly improve view performance. For example, the altscreen was previously triggered via a Cmd, which would not necessarily hit at the same moment the view was being rendered.

What else happened?

We've removed many view-based startup options and commands in favor of a more declarative approach using the View type. For example, WithMouseCellMotion() is now a property on View, i.e. view.MouseMode = tea.MouseModeCellMotion.

Now, the View() method returns a View struct that encapsulates all view-related properties. This change simplifies the API and makes it more intuitive to use with a single point of configuration for views.

The View API

Previously, Bubble Tea used functional options and commands to configure various view-related properties. This approach was flexible but led to a fragmented API surface. Should I use WithAltScreen() or EnterAltScreen? Is there a difference between them? Why can't I enable focus reporting with WithReportFocus() but not via a command?

These questions and inconsistencies led us to rethink how we handle view configuration. The new View struct consolidates all view-related properties, making it easier to understand and use with a single source of truth. I.e., this is how your view looks like, and these are its properties.

type View struct {
    Layer                     Layer        // Layer represents the content of the view
    Cursor                    *Cursor      // Position, style, color (nil = hidden)
    BackgroundColor           color.Color  // Terminal default background color
    ForegroundColor           color.Color  // Terminal default foreground color
    WindowTitle               string       // Window title
    ProgressBar               *ProgressBar // Progress bar (nil = no progressbar)
    AltScreen                 bool         // Alternate screen buffer (fullscreen mode)
    MouseMode                 MouseMode    // Mouse event mode
    ReportFocus               bool         // Focus/blur events
    DisableBracketedPasteMode bool         // Bracketed paste
    DisableKeyEnhancements    bool         // Keyboard enhancements
    KeyReleases               bool         // Key release events
    UniformKeyLayout          bool         // Uniform key layout
}

Options and Commands

We've removed many of the view-related options and commands that were previously set using functional options or commands. Instead, these properties are now part of the View struct returned by the View() method.

Removed options include:

  • WithAltScreen() โ†’ View.AltScreen = true
  • WithMouseCellMotion() โ†’ View.MouseMode = tea.MouseModeCellMotion
  • WithMouseAllMotion() โ†’ View.MouseMode = tea.MouseModeAllMotion
  • WithReportFocus() โ†’ View.ReportFocus = true
  • WithKeyReleases() โ†’ View.KeyReleases = true
  • WithUniformKeyLayout() โ†’ View.UniformKeyLayout = true
  • WithoutBracketedPaste() โ†’ View.DisableBracketedPasteMode = true
  • WithInputTTY() โ†’ use OpenTTY() and set input/output manually

Removed commands include:

  • EnterAltScreen/ExitAltScreen โ†’ View.AltScreen
  • SetBackgroundColor() โ†’ View.BackgroundColor
  • SetForegroundColor() โ†’ View.ForegroundColor
  • SetCursorColor() โ†’ View.Cursor.Color
  • SetWindowTitle() โ†’ View.WindowTitle
  • EnableMouseCellMotion/EnableMouseAllMotion/DisableMouse โ†’ View.MouseMode

Model Interface

The Model interface has been updated to make View() return a View struct instead of a string. We know that a string is often more convenient and easier to work with, however, due to the number of view-related options we wanted to support, we felt it was best to encapsulate them in a dedicated struct.

You can still use strings for your views of course! You just need to wrap them in a NewView() call, or use view.SetContent(yourString).

// Before
type Model interface {
    Init() Cmd
    Update(Msg) (Model, Cmd)
    View() string
    // Or...
    View() (string, *Cursor)
}

// After
type Model interface {
    Init() Cmd
    Update(Msg) (Model, Cmd)
    View() View
}

Example?

Let's get to the fun part! Here's a simple example that displays a text in the center of the screen using the alt-screen buffer.

package main

import (
	"fmt"
	"os"

	tea "github.com/charmbracelet/bubbletea/v2"
	"github.com/charmbracelet/lipgloss/v2"
)

type model struct {
	width, height int
}

func (m model) Init() tea.Cmd {
	return nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.WindowSizeMsg:
		m.width = msg.Width
		m.height = msg.Height
	case tea.KeyPressMsg:
		return m, tea.Quit
	}
	return m, nil
}

func (m model) View() tea.View {
	var v tea.View
	content := lipgloss.NewStyle().
		Width(m.width).
		Height(m.height).
		AlignHorizontal(lipgloss.Center).
		AlignVertical(lipgloss.Center).
		Foreground(lipgloss.Cyan).
		Render(" Bubble Tea Beta 5! ")

	v.AltScreen = true
	v.SetContent(content)

	return v
}

func main() {
	p := tea.NewProgram(model{})
	if _, err := p.Run(); err != nil {
		fmt.Fprintf(os.Stderr, "Alas, there's been an error: %v", err)
	}
}

Like details?

Hereโ€™s the full changelog since v2.0.0-beta.4

Changelog

New!

Fixed

Read more

v1.3.10

17 Sep 20:13
v1.3.10
9edf69c

Choose a tag to compare

Changelog

Bug fixes


The Charm logo

Thoughts? Questions? We love hearing from you. Feel free to reach out on X, Discord, Slack, The Fediverse, Bluesky.

v1.3.9

11 Sep 18:52
v1.3.9
ffa0502

Choose a tag to compare

Changelog

New Features

Bug fixes

Other work


The Charm logo

Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, Discord, Slack, The Fediverse.

v1.3.8

08 Sep 20:05
v1.3.8
21eecd5

Choose a tag to compare

Changelog

Bug fixes


The Charm logo

Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, Discord, Slack, The Fediverse.

v1.3.7

05 Sep 15:22
v1.3.7
28ab4f4

Choose a tag to compare

Changelog

Bug fixes

Documentation updates

Other work


The Charm logo

Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, Discord, Slack, The Fediverse.

v2.0.0-beta.4

10 Jul 18:48
v2.0.0-beta.4
81ed4ba

Choose a tag to compare

v2.0.0-beta.4 Pre-release
Pre-release

Howโ€™s it going?

This is a small beta release with a few changes and a bunch of bug fixes. Enjoy!

Keyboard

WithKeyboardEnhancements has been split into three separate functions for clarity:

  • WithKeyReleases: Enables support for reporting key release events.
  • WithUniformKeyLayout: Enables support for reporting key events in a uniform layout format.
  • RequestKeyDisambiguation: Enables support for disambiguated key events.

Debugging Panics

You can now set TEA_DEBUG to true to dump panic logs to the current directory.

export TEA_DEBUG=1

Changelog

New Features

Bug fixes

Documentation updates

Other work


The Charm logo

Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or on Discord.

v1.3.6

07 Jul 20:15
v1.3.6
0a63003

Choose a tag to compare

This version fixes some important issues regarding tea.Exec output after resuming from the executed program, and Windows losing input character on successive program execution.

Big thanks to @raphaelvigee and @joshallenit for working on these issues, you're the best!

Changelog

Bug fixes

Documentation updates

Other work

  • ca9473b: ci: sync golangci-lint config (#1431) (@github-actions[bot])

The Charm logo

Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or on Discord.