gdamore/tcell
> A pure-Go, cell-based terminal library — the rendering substrate under most Go TUIs.
GitHub repo · License: Apache-2.0
Overview
Tcell is a Go package that models a text terminal as a grid of cells, each cell holding a rune (or grapheme cluster), a foreground/background color, and style attributes. It was written by Garrett D'Amore starting in 2015 as a response to nsf/termbox-go, keeping termbox's cell abstraction but adding truecolor, richer key/mouse handling, Unicode width awareness, and native Windows support1. Everything is pure Go with no CGO, which is the reason it ports cleanly to Linux, macOS, the BSDs, Windows, WASM, and Plan 9.
Its practical significance is as a foundation, not an end-user tool. rivo/tview (and by extension k9s, and many other admin TUIs) render through tcell; so do a range of editors and dashboards. Tcell owns the hard, unglamorous layer — terminal capability negotiation, escape-sequence diffing, input parsing across incompatible terminal protocols — and exposes a small Screen interface on top. Most developers meet tcell indirectly through a higher-level framework rather than calling it directly.
The defining tension is scope. Tcell deliberately stops at the cell buffer: it gives you SetContent, an event loop, and color/style primitives, but no widgets, no layout, no state model. That minimalism is why it composes well underneath other libraries, and why using it directly for a real application means building (or importing) a widget layer yourself. It is also on its third incompatible major version (v3, December 2025), each of which changes the Go import path2.
Getting Started
go get github.com/gdamore/tcell/v3
package main
import (
"github.com/gdamore/tcell/v3"
)
func main() {
screen, err := tcell.NewScreen()
if err != nil {
panic(err)
}
if err := screen.Init(); err != nil {
panic(err)
}
defer screen.Fini() // MUST run, or the terminal is left in raw mode
style := tcell.StyleDefault.Foreground(tcell.ColorGreen)
screen.SetContent(0, 0, 'H', nil, style)
screen.SetContent(1, 0, 'i', nil, style)
screen.Show()
for {
switch ev := screen.PollEvent().(type) {
case *tcell.EventKey:
if ev.Key() == tcell.KeyEscape {
return
}
case *tcell.EventResize:
screen.Sync()
}
}
}
Architecture / How It Works
The core is the Screen interface. SetContent(x, y, ...) writes into an in-memory back buffer; Show() diffs the back buffer against the last-drawn front buffer and emits only the escape sequences needed to reconcile them. Sync() forces a full redraw, which is what you call after a resize or when the terminal state is suspect. This double-buffer + diff model is the whole reason a TUI can repaint at interactive rates without visible flicker.
Terminal capabilities come from a terminfo layer. Tcell ships a large set of terminal descriptions compiled into Go (generated by its mkinfo tool from the system terminfo database), so the common terminals work with no external files. For a $TERM it does not recognize, it can fall back to a dynamic loader that shells out to infocmp at runtime. On modern terminals it goes further and actively negotiates — querying for truecolor, the Kitty/xterm keyboard protocols, and mouse support at startup — rather than trusting terminfo alone.
Input is parsed into typed events: EventKey, EventMouse, EventResize, EventPaste, EventError. PollEvent blocks for the next one; PostEvent lets other goroutines inject events into the same queue, which is the sanctioned way to drive the UI from background work. Colors span the ANSI 16, xterm 256-color palette, and 24-bit truecolor, with TCELL_TRUECOLOR, COLORTERM, and TERM suffixes controlling detection. Character-set conversion is delegated to golang.org/x/text/encoding; the full encoding set adds roughly 2 MB, so tcell makes you register the encodings you actually need instead of bundling them all.
For tests, SimulationScreen implements the same interface against an in-memory buffer with no real terminal, so TUI logic can be asserted headlessly.
Production Notes
Fini() is not optional. Init() puts the terminal into raw mode and the alternate screen. If your program exits — including via panic — without Fini() running, the user is left with a broken, no-echo terminal. Always defer screen.Fini(), and if you spawn goroutines that can panic, recover and finalize the screen before re-raising.
Drawing is single-threaded. SetContent/Show are not safe to call concurrently from multiple goroutines. The standard pattern is one render goroutine that owns the screen, with everything else communicating via PostEvent or channels. Ignoring this produces races and corrupted output that only show under load.
Wide and combining characters. Tcell tracks display width, but the cell immediately to the right of a wide (double-width) character is reserved. Writing content into that shadow cell — or assuming one rune equals one column — yields undefined rendering. Grapheme clusters must be passed as the combining slice argument to SetContent, not as separate cells.
Capability negotiation can misfire. Some emulators answer capability queries incorrectly, producing garbled keys or colors. Tcell exposes escape hatches: TCELL_KEYBOARD_PROTOCOL, TCELL_NEGOTIATE=disable, TCELL_MOUSE=disable, and truecolor overrides, plus OptKeyboardProtocol/OptNegotiation in code. Environment variables win over application options so end users can recover from a bad terminal without a rebuild — worth documenting for anyone shipping a tcell app.
Major-version upgrades touch every import. Because Go encodes the major version in the import path, moving v1 → v2 → v3 means rewriting github.com/gdamore/tcell / .../v2 / .../v3 across the codebase, and v3 carries genuine API breaks catalogued in CHANGESv3.md. v1 is unmaintained and should not be used for anything new. Libraries built on tcell (e.g. tview) pin a specific major, so mixing majors across your dependency graph is a common source of duplicate-type confusion.
Windows. Modern Windows terminals are supported natively (no CGO), with truecolor assumed. Legacy console quirks are documented separately; if you target older Windows consoles, read README-windows.md rather than assuming POSIX behavior.
When to Use / When Not
Use when:
- You are building the rendering/input layer of a TUI and want cross-platform (including native Windows) behavior from pure Go.
- You need truecolor, precise Unicode width handling, or modern keyboard/mouse protocols.
- You are writing a widget toolkit or framework and want a stable cell buffer to build on.
Avoid when:
- You want ready-made widgets, layout, or an app architecture — reach for a higher-level library that sits on top instead.
- You only need styled line output (colors, spinners, tables) without taking over the whole screen.
- You cannot absorb the maintenance cost of a library that periodically ships breaking major versions with new import paths.
Alternatives
- charmbracelet/bubbletea — Elm-architecture TUI framework; use when you want a full application model and message loop, not just a cell buffer.
- rivo/tview — widget toolkit built on top of tcell; use when you want tables, forms, and layout out of the box.
- nsf/termbox-go — the original inspiration tcell was written to improve on; unmaintained, so use tcell instead of starting new work here.
- muesli/termenv — color/style detection and styled output only; use when you need terminal styling without owning the full screen.
- charmbracelet/lipgloss — declarative styling for terminal strings; use alongside a framework when the goal is layout/styling rather than raw terminal control.
History
| Version | Date | Notes |
|---|---|---|
| initial | 2015-09 | Repo created; cell model inspired by termbox1. |
| 1.0.0 | 2017-11 | First tagged stable release. |
| 2.0.0 | 2020-10 | Major rewrite; import path moves to .../v2. |
| 2.13.x | 2024–2025 | Long-lived v2 line, still importable and maintained. |
| 3.0.0 | 2025-12 | Breaking changes vs v1/v2; import path .../v32. |
| 3.4.0 | 2026-05 | Latest v3 minor at time of writing. |
References
- ^ Tcell README — description, feature set, and termbox lineage. https://github.com/gdamore/tcell#readme
- ^ Tcell README, "Breaking Changes in v3", and
CHANGESv3.md. https://github.com/gdamore/tcell/blob/main/CHANGESv3.md
Tags
go, tui, terminal, terminal-ui, cell-buffer, terminfo, unicode, truecolor, cross-platform, cli, library