July 27, 2026
What’s New in gt: Multi-Column Stubs, Row-Wise Summaries, and Functions Since Version 0.11
The gt package for R programming reached 1.3 and picked up several table-building features worth folding into a data workflow

By Pierre DeBois
5 min read
An R programming data workflow using the gt package version 0.11 runs fine today. I last reviewed gt 0.11 over two years ago. Feature updates has moved the gt package a long way since then, reaching 1.0.0 in April 2025 and landing at 1.3.0 in January 2026. A few of the additions in the latest version change how you structure a table. Many of those changes are worth adopting now.
This post is my catch-up post to cover the features that arrived after 0.11.
For demonstration convenience, I am using the gtcars dataset that ships with the package.
A quick recap on gt package
The gt package turns a data frame into a presentation-ready table object through the gt() function. From there you add headers, footnotes, spanners, and cell formatting through a pipeline of verbs, and the finished table renders to HTML, LaTeX, Word, RTF, or an image.
The mental model is a set of table parts, the stub, the column labels, the body, and the footer, that you compose piece by piece. The features below extend that model with new parts and new ways to summarize.
Multiple columns in the stub
The stub used to hold a single row-label column. As of version 1.1.0, the rowname_col argument accepts a vector of column names, so you can carry two or more label columns on the left side of the table.
The change lives entirely in the rowname_col argument, which now reads a character vector instead of a single name. Passing c("mfr", "model") moves both columns into the stub, where they read as a grouped left margin rather than ordinary body columns. This matters for tables where a row is identified by a pair of values, a maker and a model, a region and a store, a category and a subcategory, since it keeps that identity visually separate from the measured columns. The fmt_integer() call formats the remaining numeric columns without touching the stub.
Row-wise summaries with summary_columns()
The summary_rows() function has long aggregated down a column to produce a summary row. Version 1.2.0 added the horizontal counterpart. The summary_columns() function aggregates across selected columns within each row and adds the result as a new column.
code>>>
The summary_columns() function takes the columns to aggregate, then an aggregation expression in fns. The formula ~ mean(.) treats . as the vector of values from the selected columns in that row, so mean(.) returns the row average of city and highway mpg. The new_col_names argument sets the identifier you use to target the column later, and new_col_labels sets its display label, here wrapped in md() for bold text. Because the summary is computed immediately and added as a regular column, you can reference mpg_avg in any later gt call, a fmt_number(), a tab_style(), or a cols_move(). The filter on mpg_c and mpg_h drops the one gtcars row that carries a missing value, which keeps the average honest.
Ordering rows within groups with row_order()
Reordering row groups was already possible through the row_group_order() function. What you could not do cleanly was reorder the rows inside each group. Version 1.3.0 fills that gap with the row_order() function.
The row_order() function takes one or more bare column names to sort by, applied within each row group rather than across the whole table. Here hp sets horsepower as the sort key, and reverse = TRUE flips the order so each maker's cars run from most to least powerful. The function respects the groups defined by groupname_col, so the manufacturers stay in place while their rows rearrange internally. This pairs with row_group_order() for full control, one function orders the groups and the other orders within them.
Condensed number formatting with fmt_number_si()
Large figures crowd a table when printed in full. Version 1.2.0 added fmt_number_si() , which applies SI prefixes so a value picks up a k, M, or G marker and keeps its readable digits up front.
The fmt_number_si() function scans the targeted column, selects the prefix that keeps the mantissa in a readable range, and formats accordingly, so an MSRP of 56,000 renders as 56.0k. The decimals argument fixes how many digits follow the decimal point. An optional unit argument appends a unit after the prefix when you need one, and a scale_by argument handles conversions before formatting. For dashboards and financial tables where column width is tight, this trades exact digits for a compact, scannable figure.
The 1.0 milestone and one pipe change
The jump to 1.0.0 was more a maturity marker than a rewrite, so code written for 0.11 keeps working. Version 1.3.0 also widened numeric formatting, so fmt_number(), fmt_integer(), and fmt_currency() now handle bit64::integer64 columns, which helps when your data carries large counts read straight from a database.
One item to note if you lint your scripts. In the current development line, gt stopped exporting%>%, so you either switch to the base pipe |> or call library(magrittr) yourself. Every example in this post already uses the native pipe, which sidesteps the issue entirely.
What The gt package Means for Your Data
The formatting features you learned in 0.11 still hold, so nothing here asks you to relearn the basics. The additions worth reaching for are the structural ones: multi-column stubs for tables keyed by a pair of values, summary_columns() for per-row totals and averages, and row_order() for arranging rows inside their groups.
Adopt them where they remove a workaround you already use, a manual concatenation of two label columns, a pre-computed row average, a sort you apply before the data reaches gt. Each one moves a step you were doing by hand into the table pipeline, which keeps the code closer to the output and easier to hand off.
If you are a beginner with R programming and the gt package, you can gain some insights on how to use your data in gt in my earlier posts. You can follow the links below — start with these, and combine with this post to bring your knowledge up to date.
What's New in The GT Library Version 0.11 That Makes R Programming Data Tables Easier To Create The latest version of the gt library for R programming offers LaTeX styling, chemical notation, and new datasets
How to Use The GT Library For Better Data Tables in R Programming New updates make the table library for R programming more convenient for data scientists and business analysts
Additional Table Libraries to Support The GT Library in R Programming gtsummary and gtExtra are a small family of libraries that can augment the gt library and enhance your data tables