tview
GitHub Overview
rivo/tview
Terminal UI library with rich, interactive widgets — written in Golang
Repository:https://github.com/rivo/tview
Stars12,396
Watchers121
Forks628
Created:December 15, 2017
Language:Go
License:MIT License
Topics
golangterminal-baseduser-interface
Star History
Data as of: 7/25/2025, 11:09 AM
tview
tview is a rich TUI library for Go, built on top of the low-level tcell library. It provides an extensive set of widgets including tables, forms, lists, and modals, and is adopted by popular tools like k9s.
Features
Rich Widget Set
- Basic Widgets: Box, Button, Checkbox, DropDown
- Input Widgets: InputField, TextArea, Form
- Display Widgets: List, Table, TreeView, TextView
- Layout: Flex, Grid, Pages, Modal
- Special Widgets: ProgressBar, Application
Easy-to-Use API
- Intuitive Design: Simple widget-based API
- Method Chaining: Configure settings in sequence
- Event Handling: Simple callback-based event processing
- Focus Management: Automatic focus switching
Cross-Platform
- OS Support: Windows, macOS, Linux
- Terminal Compatibility: Works with various terminal emulators
- Unicode Support: Full Unicode support
- Mouse Support: Mouse event handling
Performance
- Efficient Rendering: Fast drawing via tcell
- Differential Updates: Only redraw necessary parts
- Lightweight: Minimal memory usage
- Thread-Safe: Supports concurrent processing
Basic Usage
Installation
go get github.com/rivo/tview
Hello World
package main
import (
"github.com/rivo/tview"
)
func main() {
// Create application
app := tview.NewApplication()
// Create text box
box := tview.NewBox().
SetBorder(true).
SetTitle("Hello, tview!").
SetTitleAlign(tview.AlignCenter)
// Run application
if err := app.SetRoot(box, true).Run(); err != nil {
panic(err)
}
}
Creating Forms
package main
import (
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
func main() {
app := tview.NewApplication()
// Create form
form := tview.NewForm().
AddInputField("Name", "", 20, nil, nil).
AddPasswordField("Password", "", 20, '*', nil).
AddCheckbox("Remember me", false, nil).
AddDropDown("Role", []string{"Admin", "User", "Guest"}, 1, nil).
AddButton("Save", func() {
// Save processing
app.Stop()
}).
AddButton("Cancel", func() {
app.Stop()
})
form.SetBorder(true).
SetTitle("Login Form").
SetTitleAlign(tview.AlignLeft)
// Exit on ESC key
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyEscape {
app.Stop()
}
return event
})
if err := app.SetRoot(form, true).EnableMouse(true).Run(); err != nil {
panic(err)
}
}
Using Tables
package main
import (
"fmt"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
func main() {
app := tview.NewApplication()
// Create table
table := tview.NewTable().
SetBorders(true).
SetSelectable(true, false)
// Set headers
headers := []string{"ID", "Name", "Age", "City"}
for i, header := range headers {
table.SetCell(0, i,
tview.NewTableCell(header).
SetTextColor(tcell.ColorYellow).
SetAlign(tview.AlignCenter).
SetSelectable(false))
}
// Set data
data := [][]string{
{"1", "Alice", "30", "New York"},
{"2", "Bob", "25", "Los Angeles"},
{"3", "Charlie", "35", "Chicago"},
{"4", "Diana", "28", "Boston"},
}
for r, row := range data {
for c, cell := range row {
table.SetCell(r+1, c,
tview.NewTableCell(cell).
SetAlign(tview.AlignLeft))
}
}
// Selection event
table.SetSelectedFunc(func(row, column int) {
if row > 0 {
name := table.GetCell(row, 1).Text
app.Stop()
fmt.Printf("Selected: %s\n", name)
}
})
// Layout
flex := tview.NewFlex().
AddItem(table, 0, 1, true).
SetDirection(tview.FlexRow)
if err := app.SetRoot(flex, true).EnableMouse(true).Run(); err != nil {
panic(err)
}
}
Building Layouts
package main
import (
"github.com/rivo/tview"
)
func main() {
app := tview.NewApplication()
// Sidebar
sidebar := tview.NewList().
AddItem("Home", "Go to home", 'h', nil).
AddItem("Settings", "Configure app", 's', nil).
AddItem("Quit", "Exit application", 'q', func() {
app.Stop()
})
sidebar.SetBorder(true).SetTitle("Menu")
// Main content
main := tview.NewTextView().
SetText("Welcome to tview!\n\nThis is the main content area.").
SetDynamicColors(true).
SetRegions(true).
SetWordWrap(true)
main.SetBorder(true).SetTitle("Content")
// Footer
footer := tview.NewTextView().
SetText("Press 'q' to quit | Tab to switch focus").
SetTextAlign(tview.AlignCenter)
footer.SetBorder(true)
// Flex layout
flex := tview.NewFlex().
AddItem(sidebar, 20, 0, true).
AddItem(tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(main, 0, 1, false).
AddItem(footer, 3, 0, false),
0, 1, false)
if err := app.SetRoot(flex, true).EnableMouse(true).Run(); err != nil {
panic(err)
}
}
Advanced Features
Modal Dialogs
func showModal(app *tview.Application, message string) {
modal := tview.NewModal().
SetText(message).
AddButtons([]string{"OK", "Cancel"}).
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
if buttonLabel == "OK" {
// OK button processing
}
app.SetRoot(mainView, true)
})
app.SetRoot(modal, false)
}
Tree View
func createTreeView() *tview.TreeView {
root := tview.NewTreeNode("Root").
SetColor(tcell.ColorRed)
tree := tview.NewTreeView().
SetRoot(root).
SetCurrentNode(root)
// Add child nodes
addNode := func(target *tview.TreeNode, name string) *tview.TreeNode {
node := tview.NewTreeNode(name).
SetReference(name).
SetSelectable(true)
target.AddChild(node)
return node
}
// Build tree structure
node1 := addNode(root, "First")
addNode(node1, "Child 1")
addNode(node1, "Child 2")
node2 := addNode(root, "Second")
addNode(node2, "Child A")
addNode(node2, "Child B")
// Selection event
tree.SetSelectedFunc(func(node *tview.TreeNode) {
reference := node.GetReference()
if reference != nil {
node.SetColor(tcell.ColorGreen)
}
})
return tree
}
Page Switching
func createPages() *tview.Pages {
pages := tview.NewPages()
// Page 1
page1 := tview.NewTextView().
SetText("This is page 1").
SetTextAlign(tview.AlignCenter)
page1.SetBorder(true).SetTitle("Page 1")
// Page 2
page2 := tview.NewTextView().
SetText("This is page 2").
SetTextAlign(tview.AlignCenter)
page2.SetBorder(true).SetTitle("Page 2")
// Add pages
pages.AddPage("page1", page1, true, true)
pages.AddPage("page2", page2, true, false)
return pages
}
Progress Bar
func createProgressBar() *tview.Box {
progressBar := tview.NewBox().
SetBorder(true).
SetTitle("Progress")
// Custom draw function
progressBar.SetDrawFunc(func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) {
// Calculate progress
progress := 0.7 // 70%
filled := int(float64(width) * progress)
// Draw progress bar
for i := 0; i < width; i++ {
if i < filled {
screen.SetContent(x+i, y+height/2, '█', nil, tcell.StyleDefault.Foreground(tcell.ColorGreen))
} else {
screen.SetContent(x+i, y+height/2, '░', nil, tcell.StyleDefault.Foreground(tcell.ColorGray))
}
}
return x, y, width, height
})
return progressBar
}
Ecosystem
Adoption in Popular Projects
- k9s: Kubernetes CLI
- lazydocker: Docker TUI
- wtf: Dashboard application
- dry: Docker management tool
Related Projects
- tcell: Low-level terminal library
- cview: Fork of tview (focuses on concurrency)
Advantages
- Rich Widgets: Comprehensive set of practical widgets
- Simple API: Intuitive and easy to learn
- Stability: Proven in many production applications
- Cross-Platform: Works on all major operating systems
- Performance: Efficient rendering
Limitations
- Customizability: Limited widget appearance customization
- Architecture: Must follow specific design patterns
- Extensibility: Creating custom widgets is complex
Comparison with Other Libraries
Feature | tview | Bubble Tea | termui |
---|---|---|---|
Widgets | Very Rich | Component-based | Dashboard-focused |
Learning Cost | Low | Medium | Low-Medium |
Flexibility | Medium | Very High | Medium |
Community | Large | Very Large | Medium |
Use Case | General | General | Dashboards |
Summary
tview is the ideal library for developers who want to quickly build rich TUI applications in Go. With its extensive widget set, simple API, and cross-platform support, it can handle a wide range of applications from enterprise applications to simple tools. It particularly excels at data-driven applications centered around forms and tables.