Toast-style alert overlays for BubbleTea v2 TUI applications.
Alerts appear at a configurable corner of your terminal UI, animate with a color fade, and disappear automatically after a set duration — with no host-side timer management required.
go get github.com/ceffo/toastRequires Go 1.26+ and charm.land/bubbletea/v2.
package main
import (
"time"
tea "charm.land/bubbletea/v2"
"github.com/ceffo/toast"
)
type model struct {
toast toast.Model
width int
height int
}
func initialModel() model {
t := toast.New(60, toast.FontUnicode, 3*time.Second).
WithPosition(toast.TopRight).
WithQueueDepth(5).
WithMinWidth(20).
WithAllowEscToClose()
return model{toast: t, width: 80, height: 24}
}
func (m model) Init() tea.Cmd {
return m.toast.Init()
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd
switch msg := msg.(type) {
case tea.KeyPressMsg:
switch msg.String() {
case "i":
cmds = append(cmds, m.toast.NewAlertCmd(toast.InfoAlertUnicode, "everything is fine"))
case "w":
cmds = append(cmds, m.toast.NewAlertCmd(toast.WarnAlertUnicode, "disk usage above 80%"))
case "e":
cmds = append(cmds, m.toast.NewAlertCmd(toast.ErrorAlertUnicode, "connection refused"))
case "esc":
// ESC guard: only quit when no alert is active.
if !m.toast.HasActiveAlert() {
return m, tea.Quit
}
}
}
var toastCmd tea.Cmd
m.toast, toastCmd = m.toast.Update(msg)
cmds = append(cmds, toastCmd)
return m, tea.Batch(cmds...)
}
func (m model) View() tea.View {
content := "Hello, world!"
return tea.View{Content: m.toast.Render(content), AltScreen: true}
}A fully working example is in example/main.go.
t := toast.New(width int, font toast.FontStyle, duration time.Duration)| Builder | Description |
|---|---|
.WithPosition(pos) |
Where alerts appear (default: zero value = TopLeft) |
.WithQueueDepth(n) |
Max queued alerts; oldest is dropped when full (default: 5) |
.WithMinWidth(n) |
Minimum alert box width in columns |
.WithAllowEscToClose() |
Let users dismiss the current alert with Esc |
TopLeft · TopCenter · TopRight · BottomLeft · BottomCenter · BottomRight
| Constant | Glyphs |
|---|---|
toast.FontASCII |
(i) (!) [!!] (?)
|
toast.FontUnicode |
ⓘ ⚠ ✘ ?
|
toast.FontNerdFont |
Nerd Font icons |
Each level ships in three font variants: InfoAlertASCII, InfoAlertUnicode, InfoAlertNerdFont,
and likewise for Warn, Error, and Debug.
var SuccessAlert = toast.AlertDefinition{
Prefix: "✓",
ForeColor: "#00FF88", // hex color string
Position: toast.TopCenter, // omit to use the model's default position
}
cmds = append(cmds, m.toast.NewAlertCmd(SuccessAlert, "deployment done"))// 1. Forward Init
func (m model) Init() tea.Cmd { return m.toast.Init() }
// 2. Forward Update (must reassign m.toast)
m.toast, toastCmd = m.toast.Update(msg)
// 3. Render — pass your fully-rendered content string, get the composited result back
return tea.View{Content: m.toast.Render(content), AltScreen: true}
// 4. Fire an alert from any Update branch
cmds = append(cmds, m.toast.NewAlertCmd(toast.WarnAlertUnicode, "something happened"))When WithAllowEscToClose() is set, pressing Esc dismisses the current alert instead of
propagating the key to your app. Use HasActiveAlert() to guard your own Esc handler:
case "esc":
if !m.toast.HasActiveAlert() {
return m, tea.Quit // safe to quit — no alert is stealing Esc
}
