| Crates.io | lipgloss-table |
| lib.rs | lipgloss-table |
| version | 0.1.1 |
| created_at | 2025-08-07 14:33:14.044629+00 |
| updated_at | 2025-11-22 13:34:04.41879+00 |
| description | A table component for terminal user interfaces, styled with Lip Gloss. |
| homepage | |
| repository | https://github.com/whit3rabbit/lipgloss-rs |
| max_upload_size | |
| id | 1785383 |
| size | 205,818 |
A table component for Terminal UIs, styled with Lip Gloss. This crate is part of the lipgloss-rs ecosystem and aims for 1:1 API and rendering parity with Charm's Go implementation.
lipgloss::StyleUse the batteries-included facade (recommended):
[dependencies]
lipgloss-extras = { version = "0.1.0", features = ["tables"] }
Or depend directly on the component (add lipgloss for styling):
[dependencies]
lipgloss-table = "0.1.0"
lipgloss = "0.1.0"
use lipgloss_table::Table;
let mut t = Table::new()
.headers(vec!["Name", "Age", "City"])
.row(vec!["Alice", "30", "New York"])
.row(vec!["Bob", "25", "London"]);
println!("{}", t.render());
Apply styles per cell with a function. Use HEADER_ROW for header styling.
use lipgloss::{Color, Style};
use lipgloss_table::{Table, HEADER_ROW};
let style_fn = |row: i32, _col: usize| {
match row {
HEADER_ROW => Style::new().bold(true).foreground(Color::from("63")),
r if r % 2 == 0 => Style::new().foreground(Color::from("238")),
_ => Style::new(),
}
};
let mut t = Table::new()
.headers(vec!["Language", "Formal", "Informal"])
.row(vec!["Japanese", "こんにちは", "やあ"])
.row(vec!["Russian", "Здравствуйте", "Привет"])
.style_func(style_fn);
println!("{}", t.render());
For complex logic, capture state with a boxed closure:
use lipgloss::{Color, Style};
use lipgloss_table::{Table, HEADER_ROW};
let highlight = Color::from("201");
let mut t = Table::new()
.headers(vec!["Status", "Message"])
.row(vec!["ERROR", "Something went wrong"])
.row(vec!["OK", "All good"])
.style_func_boxed(move |row, col| {
if row == HEADER_ROW { return Style::new().bold(true); }
if col == 0 {
match row {
0 => Style::new().foreground(highlight.clone()),
_ => Style::new(),
}
} else {
Style::new()
}
});
println!("{}", t.render());
Predefined helpers are available:
use lipgloss_table::{Table, header_row_style, zebra_style, minimal_style};
let mut t = Table::new()
.headers(vec!["Item", "Qty"])
.row(vec!["Apples", "3"])
.row(vec!["Bananas", "5"])
.style_func(zebra_style);
println!("{}", t.render());
Enable/disable borders and separators:
use lipgloss::{rounded_border, thick_border, Style, Color};
use lipgloss_table::Table;
let mut bordered = Table::new()
.headers(vec!["A", "B"])
.row(vec!["1", "2"])
.border(rounded_border())
.border_style(Style::new().foreground(Color::from("238")))
.border_row(true);
let mut minimal = Table::new()
.headers(vec!["A", "B"])
.row(vec!["1", "2"])
.border_top(false)
.border_bottom(false)
.border_left(false)
.border_right(false)
.border_header(false)
.border_column(false);
println!("{}\n\n{}", bordered.render(), minimal.render());
width(i32) to constrain total table width. Columns are resized with an intelligent expand/shrink algorithm.wrap(true) to wrap cell content; use wrap(false) to truncate with ellipsis.height(i32) and offset(usize) for paging/scrolling.use lipgloss_table::Table;
let mut wrap_demo = Table::new()
.headers(vec!["Product", "Description"])
.row(vec![
"MacBook Pro",
"Powerful laptop for developers with long descriptions that wrap",
])
.width(40)
.wrap(true);
let mut trunc_demo = Table::new()
.headers(vec!["Product", "Description"])
.row(vec![
"MacBook Pro",
"This description will be truncated rather than wrapped",
])
.width(30)
.wrap(false);
println!("{}\n\n{}", wrap_demo.render(), trunc_demo.render());
Column widths are inferred from content and style. You can set fixed widths and spacing via per-cell styles in your style function. The widest explicit width per column is respected.
use lipgloss::Style;
use lipgloss_table::{Table, HEADER_ROW};
let style_fn = |row: i32, col: usize| {
let mut s = Style::new();
if row == HEADER_ROW { s = s.bold(true); }
// Fix the first column to width 12, add padding to all cells
if col == 0 { s = s.width(12); }
s.padding(0, 1, 0, 1)
};
let mut t = Table::new()
.headers(vec!["Name", "Role"])
.row(vec!["Alice Johnson", "Engineer"])
.row(vec!["Bob", "PM"])
.style_func(style_fn)
.width(32);
println!("{}", t.render());
You can supply your own data source by implementing Data, or use the built-in StringData. Filter rows without copying using Filter:
use lipgloss_table::{rows::{StringData, Filter}, Table};
let data = StringData::new(vec![
vec!["A".into(), "1".into()],
vec!["B".into(), "2".into()],
vec!["C".into(), "3".into()],
]);
let filtered = Filter::new(data).filter(|row| row % 2 == 0); // keep even rows
let mut t = Table::new()
.headers(vec!["Key", "Val"])
.data(filtered)
.width(18);
println!("{}", t.render());
Run demos from the repository root:
cargo run --package table-demo-languages
cargo run --package table-demo-chess
cargo run --package table-demo-mindy
cargo run --package table-demo-pokemon
cargo run --package table-demo-ansi
lipgloss::width.lipgloss::Border presets (rounded, thick, block, etc.).https://docs.rs/lipgloss-tablehttps://github.com/whit3rabbit/lipgloss-rsMIT