termui
GitHub Overview
Stars13,393
Watchers285
Forks798
Created:February 3, 2015
Language:Go
License:MIT License
Topics
None
Star History
Data as of: 7/25/2025, 11:09 AM
termui
termui is a Go TUI library specialized for building dashboards and monitoring tools. It provides rich data visualization widgets such as charts, graphs, and gauges, making it ideal for system monitoring and real-time dashboard construction.
Features
Data Visualization Widgets
- Charts: LineChart, BarChart, PieChart, SparkLine
- Gauges: Gauge, Battery, Progress
- Lists: List, Table, Tree
- Text: Paragraph, Tabs
- Layout: Grid, Block
Performance and Real-time Updates
- Efficient Rendering: Differential rendering
- Real-time Data: Display streaming data
- Memory Efficient: Minimal memory footprint
- High-frequency Updates: Supports frequent data updates
Ease of Use
- Simple API: Intuitive widget creation
- Default Themes: Ready-to-use color schemes
- Responsive Layout: Adapts to screen size
- Event Handling: Keyboard and mouse events
Customizability
- Style Configuration: Customize colors, borders, padding
- Custom Widgets: Create your own widgets
- Themes: Define custom themes
- Layout System: Flexible grid layout
Basic Usage
Installation
go get github.com/gizak/termui/v3
Hello World
package main
import (
"log"
ui "github.com/gizak/termui/v3"
"github.com/gizak/termui/v3/widgets"
)
func main() {
if err := ui.Init(); err != nil {
log.Fatalf("failed to initialize termui: %v", err)
}
defer ui.Close()
// Create paragraph widget
p := widgets.NewParagraph()
p.Text = "Hello World!"
p.SetRect(0, 0, 25, 5)
p.Title = "Welcome"
p.BorderStyle.Fg = ui.ColorYellow
ui.Render(p)
// Event loop
for e := range ui.PollEvents() {
if e.Type == ui.KeyboardEvent {
break
}
}
}
Creating a Dashboard
package main
import (
"log"
"math"
"time"
ui "github.com/gizak/termui/v3"
"github.com/gizak/termui/v3/widgets"
)
func main() {
if err := ui.Init(); err != nil {
log.Fatalf("failed to initialize termui: %v", err)
}
defer ui.Close()
// Gauge
g := widgets.NewGauge()
g.Title = "CPU Usage"
g.Percent = 50
g.SetRect(0, 0, 50, 3)
g.BarColor = ui.ColorRed
g.LabelStyle = ui.NewStyle(ui.ColorYellow)
// Sparkline
sl := widgets.NewSparkline()
sl.Title = "Network"
sl.Data = []float64{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1}
sl.LineColor = ui.ColorCyan
sl.TitleStyle.Fg = ui.ColorWhite
slg := widgets.NewSparklineGroup(sl)
slg.Title = "Traffic"
slg.SetRect(0, 3, 50, 8)
// Bar chart
bc := widgets.NewBarChart()
bc.Title = "Memory Usage"
bc.SetRect(0, 8, 50, 13)
bc.Labels = []string{"S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7"}
bc.Data = []float64{12, 18, 15, 20, 22, 19, 23, 25}
bc.BarWidth = 5
bc.BarColors = []ui.Color{ui.ColorGreen, ui.ColorYellow, ui.ColorRed}
bc.LabelStyles = []ui.Style{ui.NewStyle(ui.ColorBlue)}
bc.NumStyles = []ui.Style{ui.NewStyle(ui.ColorWhite)}
// Initial render
ui.Render(g, slg, bc)
// Update timer
uiEvents := ui.PollEvents()
ticker := time.NewTicker(time.Second).C
// Event loop
for {
select {
case e := <-uiEvents:
switch e.ID {
case "q", "<C-c>":
return
}
case <-ticker:
// Update data
g.Percent = int(50 + 30*math.Sin(float64(time.Now().Unix())))
// Add data to sparkline
sl.Data = append(sl.Data[1:], sl.Data[len(sl.Data)-1]+float64(10-20*math.Sin(float64(time.Now().Unix()))))
ui.Render(g, slg, bc)
}
}
}
Real-time Charts
package main
import (
"log"
"math"
"time"
ui "github.com/gizak/termui/v3"
"github.com/gizak/termui/v3/widgets"
)
func main() {
if err := ui.Init(); err != nil {
log.Fatalf("failed to initialize termui: %v", err)
}
defer ui.Close()
// Line chart
lc := widgets.NewPlot()
lc.Title = "Sine Wave"
lc.Data = make([][]float64, 1)
lc.SetRect(0, 0, 60, 15)
lc.AxesColor = ui.ColorWhite
lc.LineColors[0] = ui.ColorGreen
lc.Marker = widgets.MarkerDot
// Data generation function
generateData := func() []float64 {
data := make([]float64, 100)
for i := 0; i < 100; i++ {
data[i] = 10 + 10*math.Sin(float64(i)/5.0+float64(time.Now().UnixNano())/1e9)
}
return data
}
lc.Data[0] = generateData()
// Pie chart
pc := widgets.NewPieChart()
pc.Title = "Resource Distribution"
pc.SetRect(60, 0, 100, 15)
pc.Data = []float64{.25, .25, .25, .25}
pc.AngleOffset = .15
pc.LabelFormatter = func(i int, v float64) string {
return fmt.Sprintf("%.0f%%", v*100)
}
ui.Render(lc, pc)
// Update loop
uiEvents := ui.PollEvents()
ticker := time.NewTicker(100 * time.Millisecond).C
for {
select {
case e := <-uiEvents:
switch e.ID {
case "q", "<C-c>":
return
}
case <-ticker:
lc.Data[0] = generateData()
ui.Render(lc)
}
}
}
Grid Layout
package main
import (
"log"
ui "github.com/gizak/termui/v3"
"github.com/gizak/termui/v3/widgets"
)
func main() {
if err := ui.Init(); err != nil {
log.Fatalf("failed to initialize termui: %v", err)
}
defer ui.Close()
// Create widgets
p1 := widgets.NewParagraph()
p1.Title = "Statistics"
p1.Text = "CPU: 45%\nMemory: 2.5GB / 8GB\nDisk: 120GB / 500GB"
p1.BorderStyle.Fg = ui.ColorYellow
p2 := widgets.NewParagraph()
p2.Title = "Logs"
p2.Text = "[INFO] System started\n[WARN] High load detected\n[INFO] Process completed"
p2.BorderStyle.Fg = ui.ColorMagenta
l := widgets.NewList()
l.Title = "Processes"
l.Rows = []string{
"[1] nginx",
"[2] postgres",
"[3] redis",
"[4] app-server",
}
l.SelectedRowStyle = ui.NewStyle(ui.ColorGreen)
l.BorderStyle.Fg = ui.ColorCyan
// Grid definition
grid := ui.NewGrid()
termWidth, termHeight := ui.TerminalDimensions()
grid.SetRect(0, 0, termWidth, termHeight)
grid.Set(
ui.NewRow(1.0/2,
ui.NewCol(1.0/2, p1),
ui.NewCol(1.0/2, p2),
),
ui.NewRow(1.0/2,
ui.NewCol(1.0, l),
),
)
ui.Render(grid)
// Event handling
previousKey := ""
uiEvents := ui.PollEvents()
for {
e := <-uiEvents
switch e.ID {
case "q", "<C-c>":
return
case "j", "<Down>":
l.ScrollDown()
case "k", "<Up>":
l.ScrollUp()
case "<C-d>":
l.ScrollHalfPageDown()
case "<C-u>":
l.ScrollHalfPageUp()
case "<C-f>":
l.ScrollPageDown()
case "<C-b>":
l.ScrollPageUp()
case "<Home>":
l.ScrollTop()
case "<End>":
l.ScrollBottom()
}
if previousKey == "g" {
switch e.ID {
case "g":
l.ScrollTop()
}
}
if e.ID == "g" {
previousKey = "g"
} else {
previousKey = ""
}
ui.Render(grid)
}
}
Advanced Features
Custom Widgets
type CustomGauge struct {
widgets.Block
Percent int
BarColor ui.Color
Label string
}
func NewCustomGauge() *CustomGauge {
return &CustomGauge{
Block: *widgets.NewBlock(),
BarColor: ui.ColorGreen,
}
}
func (g *CustomGauge) Draw(buf *ui.Buffer) {
g.Block.Draw(buf)
// Draw bar
barWidth := int(float64(g.Inner.Dx()) * float64(g.Percent) / 100)
for x := g.Inner.Min.X; x < g.Inner.Min.X+barWidth; x++ {
for y := g.Inner.Min.Y; y < g.Inner.Max.Y; y++ {
buf.SetCell(ui.NewCell('█', ui.NewStyle(g.BarColor)), image.Pt(x, y))
}
}
// Draw label
label := fmt.Sprintf("%s: %d%%", g.Label, g.Percent)
labelX := g.Inner.Min.X + (g.Inner.Dx()-len(label))/2
labelY := g.Inner.Min.Y + g.Inner.Dy()/2
for i, r := range label {
buf.SetCell(ui.NewCell(r, ui.NewStyle(ui.ColorWhite)), image.Pt(labelX+i, labelY))
}
}
Data Streaming
func streamData(dataChan chan<- float64) {
for {
// Read from actual data source
value := rand.Float64() * 100
dataChan <- value
time.Sleep(100 * time.Millisecond)
}
}
func main() {
// ... initialization code ...
dataChan := make(chan float64, 100)
go streamData(dataChan)
data := make([]float64, 50)
for {
select {
case value := <-dataChan:
// Update data
data = append(data[1:], value)
// Update chart
chart.Data[0] = data
ui.Render(chart)
case e := <-uiEvents:
if e.ID == "q" {
return
}
}
}
}
Theme Customization
// Custom theme definition
type Theme struct {
Background ui.Color
Border ui.Color
Title ui.Color
Text ui.Color
Highlight ui.Color
}
var DarkTheme = Theme{
Background: ui.ColorBlack,
Border: ui.ColorWhite,
Title: ui.ColorYellow,
Text: ui.ColorWhite,
Highlight: ui.ColorGreen,
}
func applyTheme(w *widgets.Paragraph, theme Theme) {
w.BorderStyle.Fg = theme.Border
w.TitleStyle.Fg = theme.Title
w.TextStyle.Fg = theme.Text
}
Ecosystem
Similar Projects
- termdash: More advanced dashboard framework
- tcell: Low-level terminal library
- tui-go: Deprecated but influential library
Adoption Examples
- gotop: System monitoring tool
- sampler: Configuration-based monitoring tool
- lazydocker: Docker management tool (partial use)
Advantages
- Data Visualization: Rich chart and graph widgets
- Dashboard-focused: Easy construction of monitoring tools
- Simple API: Intuitive and easy to learn
- Performance: Optimized for real-time data display
- Customizability: Create custom widgets
Limitations
- Limited Use Case: Not suitable for purposes other than dashboards
- Interactivity: Not suitable for complex input handling
- Layout: Basic grid system
- Update Frequency: Updates have stagnated since v3
Comparison with Other Libraries
Feature | termui | Bubble Tea | tview |
---|---|---|---|
Use Case | Dashboard | General | General |
Data Viz | Excellent | Needs impl | Basic |
Learning Cost | Low | Medium | Low |
Flexibility | Medium | Very High | High |
Architecture | Widget | Elm | Widget |
Summary
termui is an excellent library for building dashboards and monitoring tools in Go. With rich data visualization widgets, simple API, and efficient rendering, it's ideal for real-time data display and system monitoring. While not suitable for general-purpose TUI applications, its specialization makes it one of the most productive choices for dashboard development.