tview

TUIWidgetsTerminalCross-platformGo

GitHub Overview

rivo/tview

Terminal UI library with rich, interactive widgets — written in Golang

Stars12,396
Watchers121
Forks628
Created:December 15, 2017
Language:Go
License:MIT License

Topics

golangterminal-baseduser-interface

Star History

rivo/tview 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

FeaturetviewBubble Teatermui
WidgetsVery RichComponent-basedDashboard-focused
Learning CostLowMediumLow-Medium
FlexibilityMediumVery HighMedium
CommunityLargeVery LargeMedium
Use CaseGeneralGeneralDashboards

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.