Lightweight tables for R, with function names compatible with gt.
table provides a small grammar of tables that covers the structure most reports need — titles, column spanners, row groups, footnotes, and number formatting — without the heavy dependency stack. It targets HTML only (no LaTeX or RTF), which keeps the implementation minimal: the entire runtime is a single vanilla-JS file (about 10 KB minified).
The function names are designed to be consistent with the gt package, so users familiar with gt can get started quickly. The package is forked from lt by Yihui Xie.
# development version
ipkg::install_github("chuxinyuan/table")gt() creates a table object from a data frame. The tab_*(), cols_*(),
fmt_*(), and other functions build on it via the pipe.
Structure
tab_header()— title and optional subtitle above the table.tab_spanner()— column-spanner label spanning a group of columns; or auto-infer spanners from column name prefixes.tab_row_group()— partition rows into labeled groups (rowspan or full-width separator style), either by column values or manual row indices.
Content & labels
cols_label()— override column header labels.tab_footnote()— attach a numbered footnote to any region (title, subtitle, column, spanner, group, or body cells).tab_source_note()— append a plain unnumbered note below the table.fmt_markdown()— render selected columns' cells as raw HTML instead of escaping them. For raw HTML in text (title, labels, footnotes, ...), wrap the text inI()in the corresponding function.
Formatting
fmt_number()— numeric formatting: decimal places, thousand separators, prefix/suffix, percentage, scientific notation, etc.fmt_date()— date/datetime formatting using the browser locale.sub_values()— substitute specific values (e.g., replace0with"—"orNAwith"n/a").cols_merge()— merge several columns into one using a sprintf-style pattern.tab_stub_indent()— indent selected rows (useful for hierarchical row labels).
Appearance
cols_align()— set column alignment (left / center / right).cols_width()— set column widths.tab_style()— apply CSS classes or inline styles to cells, conditionally or unconditionally.opt_css()— attach an external CSS file or URL to the table.
Column order
cols_move()— reorder columns.
Export
gtsave()— save a table to a file:.html(optionally baking a static<table>that needs no JavaScript to view),.pdf, or.png(rendered via a headless Chromium browser).
Shiny
gt_output()/render_gt()— Shiny UI and server bindings.
The R code below builds a table spec. Under the hood table serializes it
to a compact JSON object and ships it to the browser, where a tiny
vanilla-JS runtime renders the <table>.
library(table)
d = data.frame(
Group = c("Treatment", "Treatment", "Control", "Control"),
Endpoint = c("Primary", "Secondary", "Primary", "Secondary"),
Estimate = c(0.6123, 0.7891, 0.4567, 0.5432),
CI_Lower = c(0.4012, 0.5678, 0.2345, 0.3210),
CI_Upper = c(0.8234, 1.0104, 0.6789, 0.7654),
P_Value = c(0.0012, NA, 0.1234, NA)
)
gt(d) |>
tab_row_group(~ Group) |>
tab_header("Study Results", "Primary and secondary endpoints") |>
tab_spanner(`95% CI` ~ CI_Lower + CI_Upper) |>
fmt_number(~ Estimate + CI_Lower + CI_Upper, decimals = 3) |>
sub_values(~ P_Value, missing = "—") |>
tab_footnote("Two-sided p-value from log-rank test.", "column", ~ P_Value)The same table can be built directly in JavaScript. Load lt.js once on the
page, then call LT.build() from an inline <script> with the JSON spec:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xiee/utils/css/lt.min.css">
<script src="https://cdn.jsdelivr.net/npm/@xiee/utils/js/lt.min.js"></script><script>
LT.build({
"data": {
"Group": ["Treatment", "Treatment", "Control", "Control"],
"Endpoint": ["Primary", "Secondary", "Primary", "Secondary"],
"Estimate": [0.6123, 0.7891, 0.4567, 0.5432],
"CI_Lower": [0.4012, 0.5678, 0.2345, 0.3210],
"CI_Upper": [0.8234, 1.0104, 0.6789, 0.7654],
"P_Value": [0.0012, null, 0.1234, null]
},
"ops": [
{ "type": "fmt_number", "columns": ["Estimate", "CI_Lower", "CI_Upper"], "decimals": 3 },
{ "type": "sub", "columns": ["P_Value"], "missing": "—" }
],
"row_group": ["Group"],
"header": { "title": "Study Results", "subtitle": "Primary and secondary endpoints" },
"spanners": [{ "label": "95% CI", "columns": ["CI_Lower", "CI_Upper"] }],
"footnotes": [{
"text": "Two-sided p-value from log-rank test.",
"location": { "type": "column_labels", "columns": ["P_Value"] }
}]
});
</script>LT.build() renders the table in place of the calling <script> tag. One
lt.js inclusion handles any number of tables on the page.
An example that is a bit more complex:
library(dplyr)
library(table)
mtcars |>
tibble::rownames_to_column("model") |>
select(
model, cyl, disp,
hp, mpg, wt, qsec
) |>
head(10) |>
mutate(
cyl = paste(cyl, "缸车")
) -> df
df |>
gt(groupname_col = "cyl") |>
# 1.表格标题 & 副标题
tab_header(
title = "table 包功能全面展示",
subtitle = "基于 mtcars 数据(前 10 车型)"
) |>
# 2.来源注释 & 脚注
tab_source_note(
text = "数据来源:1974 年《Motor Trend》杂志"
) |>
tab_footnote(
text = "立方英尺。",
where = "column",
columns ~ disp
) |>
tab_footnote(
text = "油耗(英里/加仑)大于 20 的车型",
where = "body",
columns = "mpg",
rows = which(df$mpg > 20)
) |>
# 3.列组(Spanners)
tab_spanner(
label = "动力性能",
columns = c("disp", "hp")
) |>
tab_spanner(
label = "经济性与操控",
columns = c("mpg", "wt", "qsec")
) |>
# 4.行组(row group)
tab_row_group(~ cyl) |>
# 5.数值列的格式化(小数、千分位、货币等)
fmt_number(
columns = c("disp", "hp", "mpg"),
decimals = 1,
big_mark = ","
) |>
fmt_number(
columns = "wt",
decimals = 2,
suffix = "t"
) |>
fmt_number(
columns = "qsec",
decimals = 2
) |>
# 6.自定义单元格样式(加粗、背景色等)
tab_style(
columns = "model",
rows = which(df$model == "Mazda RX4"),
bold = TRUE,
bg = "#fefedf",
borderBottom = "2px solid #4a4"
) |>
tab_style(
columns = "qsec",
rows = which(df$qsec < 17),
italic = TRUE,
color = "red"
) |>
# 7.条件格式(功能缺失)
# 8.nanoplot 图(功能缺失)
# 9.最后调整列宽(以像素为单位)
cols_width(
cyl = "120px",
model = "130px",
disp = "120px",
hp = "90px",
mpg = "100px",
wt = "90px",
qsec = "150px"
) |>
# 10.修改列标签
cols_label(
cyl = "缸数(cyl)",
model = "车型(model)",
disp = "排量(disp)",
hp = "马力(hp)",
mpg = "油耗(mpg)",
wt = "重量(wt)",
qsec = "1/4英里时间(qsec)"
) -> table
print(table)This package is forked from lt by Yihui Xie. lt is directly inspired by gt by Rich Iannone and the RStudio/Posit team. The grammar of tables that gt pioneered — layering titles, spanners, footnotes, and formatters onto a data frame — is a great idea; this package aims to provide a minimal re-implementation for contexts where a lighter footprint is preferred, with function names consistent with gt for ease of adoption.