https://github.com/aquasecurity/table
:abacus: Tables for terminals, in Go.
https://github.com/aquasecurity/table
go table terminal tui
Last synced: 9 months ago
JSON representation
:abacus: Tables for terminals, in Go.
- Host: GitHub
- URL: https://github.com/aquasecurity/table
- Owner: aquasecurity
- License: mit
- Created: 2022-05-12T08:18:48.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2025-07-13T11:43:17.000Z (12 months ago)
- Last Synced: 2025-07-13T13:23:05.677Z (12 months ago)
- Topics: go, table, terminal, tui
- Language: Go
- Homepage: https://pkg.go.dev/github.com/aquasecurity/table
- Size: 146 KB
- Stars: 110
- Watchers: 9
- Forks: 7
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# table: Tables for terminals
This is a Go module for rendering tables in the terminal.

## Features
- :arrow_up_down: Headers/footers
- :leftwards_arrow_with_hook: Text wrapping
- :twisted_rightwards_arrows: Auto-merging of cells
- :interrobang: Customisable line/border characters
- :rainbow: Customisable line/border colours
- :play_or_pause_button: Individually enable/disable borders, row lines
- :left_right_arrow: Set alignments on a per-column basis, with separate settings for headers/footers
- :triangular_ruler: Intelligently wrap/pad/measure ANSI coloured input
- :dancers: Support for double-width unicode characters
- :bar_chart: Load data from CSV files
Check out the [documentation](https://pkg.go.dev/github.com/aquasecurity/table) for full features/usage.
## Examples
### Example: Basic
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetHeaders("ID", "Fruit", "Stock")
t.AddRow("1", "Apple", "14")
t.AddRow("2", "Banana", "88,041")
t.AddRow("3", "Cherry", "342")
t.AddRow("4", "Dragonfruit", "1")
t.Render()
}
```
#### Output
```
┌────┬─────────────┬────────┐
│ ID │ Fruit │ Stock │
├────┼─────────────┼────────┤
│ 1 │ Apple │ 14 │
├────┼─────────────┼────────┤
│ 2 │ Banana │ 88,041 │
├────┼─────────────┼────────┤
│ 3 │ Cherry │ 342 │
├────┼─────────────┼────────┤
│ 4 │ Dragonfruit │ 1 │
└────┴─────────────┴────────┘
```
### Example: No Row Lines
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetRowLines(false)
t.SetHeaders("ID", "Fruit", "Stock")
t.AddRow("1", "Apple", "14")
t.AddRow("2", "Banana", "88,041")
t.AddRow("3", "Cherry", "342")
t.AddRow("4", "Dragonfruit", "1")
t.Render()
}
```
#### Output
```
┌────┬─────────────┬────────┐
│ ID │ Fruit │ Stock │
├────┼─────────────┼────────┤
│ 1 │ Apple │ 14 │
│ 2 │ Banana │ 88,041 │
│ 3 │ Cherry │ 342 │
│ 4 │ Dragonfruit │ 1 │
└────┴─────────────┴────────┘
```
### Example: No Borders
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetBorders(false)
t.SetHeaders("ID", "Fruit", "Stock")
t.AddRow("1", "Apple", "14")
t.AddRow("2", "Banana", "88,041")
t.AddRow("3", "Cherry", "342")
t.AddRow("4", "Dragonfruit", "1")
t.Render()
}
```
#### Output
```
ID │ Fruit │ Stock
────┼─────────────┼────────
1 │ Apple │ 14
────┼─────────────┼────────
2 │ Banana │ 88,041
────┼─────────────┼────────
3 │ Cherry │ 342
────┼─────────────┼────────
4 │ Dragonfruit │ 1
```
### Example: No Borders Or Row Lines
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetRowLines(false)
t.SetBorders(false)
t.SetHeaders("ID", "Fruit", "Stock")
t.AddRow("1", "Apple", "14")
t.AddRow("2", "Banana", "88,041")
t.AddRow("3", "Cherry", "342")
t.AddRow("4", "Dragonfruit", "1")
t.Render()
}
```
#### Output
```
ID │ Fruit │ Stock
────┼─────────────┼────────
1 │ Apple │ 14
2 │ Banana │ 88,041
3 │ Cherry │ 342
4 │ Dragonfruit │ 1
```
### Example: Specific Borders
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetRowLines(false)
t.SetBorderLeft(true)
t.SetBorderRight(false)
t.SetBorderTop(true)
t.SetBorderBottom(false)
t.SetHeaders("ID", "Fruit", "Stock")
t.AddRow("1", "Apple", "14")
t.AddRow("2", "Banana", "88,041")
t.AddRow("3", "Cherry", "342")
t.AddRow("4", "Dragonfruit", "1")
t.Render()
}
```
#### Output
```
┌────┬─────────────┬────────
│ ID │ Fruit │ Stock
├────┼─────────────┼────────
│ 1 │ Apple │ 14
│ 2 │ Banana │ 88,041
│ 3 │ Cherry │ 342
│ 4 │ Dragonfruit │ 1
```
### Example: Footers
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetHeaders("ID", "Fruit", "Stock")
t.SetRowLines(false)
t.AddRow("1", "Apple", "14")
t.AddRow("2", "Banana", "88,041")
t.AddRow("3", "Cherry", "342")
t.AddRow("4", "Dragonfruit", "1")
t.SetFooters("", "Count", "4")
t.Render()
}
```
#### Output
```
┌────┬─────────────┬────────┐
│ ID │ Fruit │ Stock │
├────┼─────────────┼────────┤
│ 1 │ Apple │ 14 │
│ 2 │ Banana │ 88,041 │
│ 3 │ Cherry │ 342 │
│ 4 │ Dragonfruit │ 1 │
├────┼─────────────┼────────┤
│ │ Count │ 4 │
└────┴─────────────┴────────┘
```
### Example: Padding
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetPadding(5)
t.SetHeaders("ID", "Fruit", "Stock")
t.AddRow("1", "Apple", "14")
t.AddRow("2", "Banana", "88,041")
t.AddRow("3", "Cherry", "342")
t.AddRow("4", "Dragonfruit", "1")
t.Render()
}
```
#### Output
```
┌────────────┬─────────────────────┬────────────────┐
│ ID │ Fruit │ Stock │
├────────────┼─────────────────────┼────────────────┤
│ 1 │ Apple │ 14 │
├────────────┼─────────────────────┼────────────────┤
│ 2 │ Banana │ 88,041 │
├────────────┼─────────────────────┼────────────────┤
│ 3 │ Cherry │ 342 │
├────────────┼─────────────────────┼────────────────┤
│ 4 │ Dragonfruit │ 1 │
└────────────┴─────────────────────┴────────────────┘
```
### Example: Alignment
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetAlignment(table.AlignLeft, table.AlignCenter, table.AlignRight)
t.SetHeaders("ID", "Fruit", "Stock")
t.AddRow("1", "Apple", "14")
t.AddRow("2", "Banana", "88,041")
t.AddRow("3", "Cherry", "342")
t.AddRow("4", "Dragonfruit", "1")
t.Render()
}
```
#### Output
```
┌────┬─────────────┬────────┐
│ ID │ Fruit │ Stock │
├────┼─────────────┼────────┤
│ 1 │ Apple │ 14 │
├────┼─────────────┼────────┤
│ 2 │ Banana │ 88,041 │
├────┼─────────────┼────────┤
│ 3 │ Cherry │ 342 │
├────┼─────────────┼────────┤
│ 4 │ Dragonfruit │ 1 │
└────┴─────────────┴────────┘
```
### Example: Rounded Corners
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetDividers(table.UnicodeRoundedDividers)
t.SetHeaders("ID", "Fruit", "Stock")
t.AddRow("1", "Apple", "14")
t.AddRow("2", "Banana", "88,041")
t.AddRow("3", "Cherry", "342")
t.AddRow("4", "Dragonfruit", "1")
t.Render()
}
```
#### Output
```
╭────┬─────────────┬────────╮
│ ID │ Fruit │ Stock │
├────┼─────────────┼────────┤
│ 1 │ Apple │ 14 │
├────┼─────────────┼────────┤
│ 2 │ Banana │ 88,041 │
├────┼─────────────┼────────┤
│ 3 │ Cherry │ 342 │
├────┼─────────────┼────────┤
│ 4 │ Dragonfruit │ 1 │
╰────┴─────────────┴────────╯
```
### Example: Custom Dividers
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetDividers(table.Dividers{
ALL: "@",
NES: "@",
NSW: "@",
NEW: "@",
ESW: "@",
NE: "@",
NW: "@",
SW: "@",
ES: "@",
EW: "~",
NS: "!",
})
t.SetHeaders("ID", "Fruit", "Stock")
t.AddRow("1", "Apple", "14")
t.AddRow("2", "Banana", "88,041")
t.AddRow("3", "Cherry", "342")
t.AddRow("4", "Dragonfruit", "1")
t.Render()
}
```
#### Output
```
@~~~~@~~~~~~~~~~~~~@~~~~~~~~@
! ID ! Fruit ! Stock !
@~~~~@~~~~~~~~~~~~~@~~~~~~~~@
! 1 ! Apple ! 14 !
@~~~~@~~~~~~~~~~~~~@~~~~~~~~@
! 2 ! Banana ! 88,041 !
@~~~~@~~~~~~~~~~~~~@~~~~~~~~@
! 3 ! Cherry ! 342 !
@~~~~@~~~~~~~~~~~~~@~~~~~~~~@
! 4 ! Dragonfruit ! 1 !
@~~~~@~~~~~~~~~~~~~@~~~~~~~~@
```
### Example: Auto Merge Cells
```go
package main
import (
"os"
"time"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetAutoMerge(true)
t.SetHeaders("System", "Status", "Last Check")
t.AddRow("Life Support", "OK", time.Now().Format(time.Stamp))
t.AddRow("Nuclear Generator", "OK", time.Now().Add(-time.Minute).Format(time.Stamp))
t.AddRow("Weapons Systems", "FAIL", time.Now().Format(time.Stamp))
t.AddRow("Shields", "OK", time.Now().Format(time.Stamp))
t.Render()
}
```
#### Output
```
┌───────────────────┬────────┬────────────────┐
│ System │ Status │ Last Check │
├───────────────────┼────────┼────────────────┤
│ Life Support │ OK │ May 2 09:28:05 │
├───────────────────┤ ├────────────────┤
│ Nuclear Generator │ │ May 2 09:27:05 │
├───────────────────┼────────┼────────────────┤
│ Weapons Systems │ FAIL │ May 2 09:28:05 │
├───────────────────┼────────┤ │
│ Shields │ OK │ │
└───────────────────┴────────┴────────────────┘
```
### Example: Load Data From Csv
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
f, err := os.Open("./_examples/12-load-data-from-csv/data.csv")
if err != nil {
panic(err)
}
t := table.New(os.Stdout)
if err := t.LoadCSV(f, true); err != nil {
panic(err)
}
t.Render()
}
```
#### Output
```
┌────┬────────────┬────────────────────────────────────────────┐
│ Id │ Date │ Message │
├────┼────────────┼────────────────────────────────────────────┤
│ 1 │ 2022-05-12 │ Hello world! │
├────┼────────────┼────────────────────────────────────────────┤
│ 2 │ 2022-05-12 │ These messages are loaded from a CSV file. │
├────┼────────────┼────────────────────────────────────────────┤
│ 3 │ 2022-05-13 │ Incredible! │
└────┴────────────┴────────────────────────────────────────────┘
```
### Example: Markdown Format
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetDividers(table.MarkdownDividers)
t.SetBorderTop(false)
t.SetBorderBottom(false)
t.SetRowLines(false)
t.SetHeaders("ID", "Fruit", "Stock")
t.AddRow("1", "Apple", "14")
t.AddRow("2", "Banana", "88,041")
t.AddRow("3", "Cherry", "342")
t.AddRow("4", "Dragonfruit", "1")
t.Render()
}
```
#### Output
```
| ID | Fruit | Stock |
|----|-------------|--------|
| 1 | Apple | 14 |
| 2 | Banana | 88,041 |
| 3 | Cherry | 342 |
| 4 | Dragonfruit | 1 |
```
### Example: Header Colspans
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetHeaders("Namespace", "Resource", "Vulnerabilities", "Misconfigurations")
t.AddHeaders("Namespace", "Resource", "Critical", "High", "Medium", "Low", "Unknown", "Critical", "High", "Medium", "Low", "Unknown")
t.SetHeaderColSpans(0, 1, 1, 5, 5)
t.SetAutoMergeHeaders(true)
t.AddRow("default", "Deployment/app", "2", "5", "7", "8", "0", "0", "3", "5", "19", "0")
t.AddRow("default", "Ingress/test", "-", "-", "-", "-", "-", "1", "0", "2", "17", "0")
t.AddRow("default", "Service/test", "0", "0", "0", "1", "0", "3", "0", "4", "9", "0")
t.Render()
}
```
#### Output
```
┌───────────┬────────────────┬──────────────────────────────────────────┬──────────────────────────────────────────┐
│ Namespace │ Resource │ Vulnerabilities │ Misconfigurations │
│ │ ├──────────┬──────┬────────┬─────┬─────────┼──────────┬──────┬────────┬─────┬─────────┤
│ │ │ Critical │ High │ Medium │ Low │ Unknown │ Critical │ High │ Medium │ Low │ Unknown │
├───────────┼────────────────┼──────────┼──────┼────────┼─────┼─────────┼──────────┼──────┼────────┼─────┼─────────┤
│ default │ Deployment/app │ 2 │ 5 │ 7 │ 8 │ 0 │ 0 │ 3 │ 5 │ 19 │ 0 │
├───────────┼────────────────┼──────────┼──────┼────────┼─────┼─────────┼──────────┼──────┼────────┼─────┼─────────┤
│ default │ Ingress/test │ - │ - │ - │ - │ - │ 1 │ 0 │ 2 │ 17 │ 0 │
├───────────┼────────────────┼──────────┼──────┼────────┼─────┼─────────┼──────────┼──────┼────────┼─────┼─────────┤
│ default │ Service/test │ 0 │ 0 │ 0 │ 1 │ 0 │ 3 │ 0 │ 4 │ 9 │ 0 │
└───────────┴────────────────┴──────────┴──────┴────────┴─────┴─────────┴──────────┴──────┴────────┴─────┴─────────┘
```
### Example: Only Wrap When Needed
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetHeaders("ID", "Fruit", "Stock")
t.AddRow("1", "01234567890123456789012345678901234567890123456789012345678901234567890123456789", "14")
t.AddRow("2", "Banana", "88,041")
t.AddRow("3", "Cherry", "342")
t.AddRow("4", "Dragonfruit", "1")
t.Render()
}
```
#### Output
```
┌────┬──────────────────────────────────────────────────────────────┬────────┐
│ ID │ Fruit │ Stock │
├────┼──────────────────────────────────────────────────────────────┼────────┤
│ 1 │ 01234567890123456789012345678901234567890123456789012345678- │ 14 │
│ │ 901234567890123456789 │ │
├────┼──────────────────────────────────────────────────────────────┼────────┤
│ 2 │ Banana │ 88,041 │
├────┼──────────────────────────────────────────────────────────────┼────────┤
│ 3 │ Cherry │ 342 │
├────┼──────────────────────────────────────────────────────────────┼────────┤
│ 4 │ Dragonfruit │ 1 │
└────┴──────────────────────────────────────────────────────────────┴────────┘
```
## Example: Double-width Unicode
```go
package main
import (
"os"
"github.com/aquasecurity/table"
)
func main() {
t := table.New(os.Stdout)
t.SetHeaders("A", "B", "C")
t.AddRow("🔥 unicode 🔥 characters 🔥", "2", "3")
t.AddRow("4", "5", "6")
t.Render()
}
```
#### Output

## Example: ANSI Colours
```go
package main
import (
"os"
"github.com/aquasecurity/table"
"github.com/liamg/tml"
)
func main() {
t := table.New(os.Stdout)
t.SetHeaders("ID", "Fruit", "Stock", "Description")
t.SetHeaderStyle(table.StyleBold)
t.SetLineStyle(table.StyleBlue)
t.SetDividers(table.UnicodeRoundedDividers)
t.AddRow("1", tml.Sprintf("Apple"), "14", tml.Sprintf("An apple is an edible fruit produced by an apple tree (Malus domestica). "))
t.AddRow("2", tml.Sprintf("Banana"), "88,041", "A banana is an elongated, edible fruit - botanically a berry.")
t.AddRow("3", tml.Sprintf("Cherry"), "342", "A cherry is the fruit of many plants of the genus Prunus, and is a fleshy drupe (stone fruit). ")
t.AddRow("4", tml.Sprintf("Dragonfruit"), "1", "A dragonfruit is the fruit of several different cactus species indigenous to the Americas.")
t.Render()
}
```
#### Output
