library(sumExtras)
library(gtsummary)
library(dplyr)
library(gt)
# Apply the recommended JAMA theme
use_jama_theme()While extras() handles basic table formatting, sumExtras
provides additional tools for creating visually polished,
publication-ready tables. This vignette covers advanced styling
techniques that go beyond the basics.
You’ll learn how to:
For basic table creation and labeling, see:
vignette("sumExtras-intro") - Getting started with
sumExtrasvignette("labeling") - Automatic variable labeling
workflowsWhen tables contain many variables, organizing them into logical
sections improves readability. The
add_variable_group_header() function from gtsummary creates
section headers, and sumExtras provides tools to style them
effectively.
trial |>
select(age, marker, grade, stage, response, trt) |>
tbl_summary(by = trt) |>
extras() |>
add_variable_group_header(
header = "Demographics",
variables = age
) |>
add_variable_group_header(
header = "Clinical Measures",
variables = marker:response
)| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Demographics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Clinical Measures | ||||
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| Tumor Response | 61 (32%) | 28 (29%) | 33 (34%) | 0.530 |
| Unknown | 7 | 3 | 4 | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
This creates section headers but they don’t stand out much from regular variables. Let’s fix that.
add_group_styling()The add_group_styling() function adds visual emphasis to
group headers, making section breaks clear and improving table
readability.
By default, add_group_styling() applies both bold and
italic formatting:
| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Demographics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Clinical Measures | ||||
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| Tumor Response | 61 (32%) | 28 (29%) | 33 (34%) | 0.530 |
| Unknown | 7 | 3 | 4 | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Demographics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Clinical Measures | ||||
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| Tumor Response | 61 (32%) | 28 (29%) | 33 (34%) | 0.530 |
| Unknown | 7 | 3 | 4 | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
Notice how the group headers now stand out with bold italic text, creating clear visual breaks between table sections.
You can control the text formatting using the format
argument:
trial |>
select(age, marker, grade, stage, trt) |>
tbl_summary(by = trt) |>
extras() |>
add_variable_group_header(
header = "Patient Characteristics",
variables = age:stage
) |>
add_group_styling(format = "bold")| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Patient Characteristics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
trial |>
select(age, marker, grade, stage, trt) |>
tbl_summary(by = trt) |>
extras() |>
add_variable_group_header(
header = "Patient Characteristics",
variables = age:stage
) |>
add_group_styling(format = "italic")| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Patient Characteristics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
trial |>
select(age, marker, grade, stage, trt) |>
tbl_summary(by = trt) |>
extras() |>
add_variable_group_header(
header = "Patient Characteristics",
variables = age:stage
) |>
add_group_styling(format = c("bold", "italic"))| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Patient Characteristics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
Using add_group_styling() also restores the original
left-justified variable label indentation. The default behavior of
add_variable_group_header() aligns variable labels with
categorical levels or “Unknown” display, which can look unbalanced.
add_group_styling() fixes this automatically.
Compare these two approaches:
Indentation may look off
| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Patient Variables | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Patient Variables | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
For additional visual emphasis, you can add background colors to
group headers using the add_group_colors() convenience
function.
trial |>
select(age, marker, grade, stage, response, trt) |>
tbl_summary(by = trt) |>
extras() |>
add_variable_group_header(
header = "Demographics",
variables = age
) |>
add_variable_group_header(
header = "Clinical Measures",
variables = marker:response
) |>
add_group_styling() |>
add_group_colors() # Default light gray background| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Demographics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Clinical Measures | ||||
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| Tumor Response | 61 (32%) | 28 (29%) | 33 (34%) | 0.530 |
| Unknown | 7 | 3 | 4 | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
This applies text formatting (bold/italic) with a light gray background (#E8E8E8) to create clear visual separation between table sections.
get_group_rows()The get_group_rows() function identifies which rows in
your gtsummary table contain group headers:
# Create a table with groups
grouped_table <- trial |>
select(age, marker, grade, stage, trt) |>
tbl_summary(by = trt) |>
extras() |>
add_variable_group_header(
header = "Demographics",
variables = age
) |>
add_variable_group_header(
header = "Disease Measures",
variables = marker:stage
)
# Get row numbers
group_rows <- get_group_rows(grouped_table)
group_rows
#> [1] 1 4These row numbers correspond to the body rows in the resulting gt
table, which you can then target with gt::tab_style().
You can specify any color with the color argument:
trial |>
select(age, marker, grade, stage, trt) |>
tbl_summary(by = trt) |>
extras() |>
add_variable_group_header(
header = "Baseline Characteristics",
variables = age:stage
) |>
add_group_styling() |>
add_group_colors(color = "#E3F2FD") # Light blue| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Baseline Characteristics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
For most use cases, add_group_colors() provides the
simplest approach. However, understanding the manual pattern helps when
you need complex gt styling beyond simple background colors.
my_table <- trial |>
select(age, marker, grade, stage, trt) |>
tbl_summary(by = trt) |>
extras() |>
add_variable_group_header(
header = "Baseline Characteristics",
variables = age:stage
) |>
add_group_styling()
group_rows <- get_group_rows(my_table)
my_table |>
as_gt() |>
gt::tab_style(
style = gt::cell_fill(color = "#E3F2FD"),
locations = gt::cells_body(rows = group_rows)
)| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Baseline Characteristics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Baseline Characteristics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
Both approaches produce identical results, but
add_group_colors() is significantly cleaner—no intermediate
objects, no manual get_group_rows() call, fully
pipeable.
Use add_group_colors() when:
Use the manual pattern when:
gt::tab_style()
operationsThe most effective tables often combine multiple styling approaches. Here are some common patterns.
For tables in manuscripts where minimalist design is preferred:
trial |>
select(age, marker, grade, stage, response, trt) |>
tbl_summary(by = trt, missing = "no") |>
extras() |>
add_variable_group_header(
header = "Demographics",
variables = age:marker
) |>
add_variable_group_header(
header = "Tumor Characteristics",
variables = grade:response
) |>
add_group_styling(format = "bold") # Bold only, no italic| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Demographics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Tumor Characteristics | ||||
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| Tumor Response | 61 (32%) | 28 (29%) | 33 (34%) | 0.530 |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
For presentations or reports where visual clarity is paramount:
my_table <- trial |>
select(age, marker, grade, stage, response, trt) |>
tbl_summary(by = trt, missing = "no") |>
extras() |>
add_variable_group_header(
header = "DEMOGRAPHICS",
variables = age:marker
) |>
add_variable_group_header(
header = "TUMOR CHARACTERISTICS",
variables = grade:response
) |>
add_group_styling() # Bold + italic
group_rows <- get_group_rows(my_table)
my_table |>
as_gt() |>
gt::tab_style(
style = list(
gt::cell_fill(color = "#E8E8E8"),
gt::cell_text(weight = "bold")
),
locations = gt::cells_body(rows = group_rows)
)| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| DEMOGRAPHICS | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| TUMOR CHARACTERISTICS | ||||
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| Tumor Response | 61 (32%) | 28 (29%) | 33 (34%) | 0.530 |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
For complex tables with many grouped sections:
# Create a more complex grouping
my_table <- trial |>
select(trt, age, marker, grade, stage, response, death) |>
tbl_summary(by = trt, missing = "no") |>
extras() |>
add_variable_group_header(
header = "Baseline Demographics",
variables = age
) |>
add_variable_group_header(
header = "Biomarkers",
variables = marker
) |>
add_variable_group_header(
header = "Disease Characteristics",
variables = grade:stage
) |>
add_variable_group_header(
header = "Outcomes",
variables = response:death
) |>
add_group_styling()
group_rows <- get_group_rows(my_table)
my_table |>
as_gt() |>
gt::tab_style(
style = gt::cell_fill(color = "#F5F5F5"),
locations = gt::cells_body(rows = group_rows)
)| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Baseline Demographics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Biomarkers | ||||
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Disease Characteristics | ||||
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| Outcomes | ||||
| Tumor Response | 61 (32%) | 28 (29%) | 33 (34%) | 0.530 |
| Patient Died | 112 (56%) | 52 (53%) | 60 (59%) | 0.412 |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
Consistent styling across tables in a document or project requires thoughtful theme management. sumExtras provides tools for both gtsummary and gt tables.
use_jama_theme()The use_jama_theme() function applies the JAMA compact
theme to all gtsummary tables in your session:
# Apply JAMA compact theme (typically done once at the beginning)
use_jama_theme()
#> Setting theme "Compact"
#> Applied JAMA compact theme to {gtsummary}
# Now all gtsummary tables use this theme
trial |>
tbl_summary(by = trt) |>
extras()| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| Tumor Response | 61 (32%) | 28 (29%) | 33 (34%) | 0.530 |
| Unknown | 7 | 3 | 4 | |
| Patient Died | 112 (56%) | 52 (53%) | 60 (59%) | 0.412 |
| Months to Death/Censor | 22.4 (15.9, 24.0) | 23.5 (17.4, 24.0) | 21.2 (14.5, 24.0) | 0.145 |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
This is equivalent to:
But use_jama_theme() is more concise and memorable.
The JAMA compact theme provides:
theme_gt_compact()When mixing gtsummary tables with regular gt tables in the same
document, visual consistency matters. The
theme_gt_compact() function applies JAMA-style compact
formatting to gt tables, matching the appearance of the JAMA compact
theme used by gtsummary.
| Chemotherapy Treatment | Age | Grade |
|---|---|---|
| Drug A | 23 | II |
| Drug B | 9 | I |
| Drug A | 31 | II |
| Drug A | NA | III |
| Drug A | 51 | III |
| Drug B | 39 | I |
| Drug A | 37 | II |
| Drug A | 32 | I |
| Drug A | 31 | II |
| Drug B | 34 | I |
| Chemotherapy Treatment | Age | Grade |
|---|---|---|
| Drug A | 23 | II |
| Drug B | 9 | I |
| Drug A | 31 | II |
| Drug A | NA | III |
| Drug A | 51 | III |
| Drug B | 39 | I |
| Drug A | 37 | II |
| Drug A | 32 | I |
| Drug A | 31 | II |
| Drug B | 34 | I |
The compact theme creates visual consistency with gtsummary tables, resulting in a more professional, cohesive document.
theme_gt_compact() provides a solid foundation, but you
can customize further:
trial |>
select(trt, age, grade, marker) |>
head(8) |>
gt::gt() |>
theme_gt_compact() |>
gt::tab_header(
title = "Trial Patient Sample",
subtitle = "First 8 patients"
) |>
gt::tab_style(
style = gt::cell_fill(color = "#F0F0F0"),
locations = gt::cells_body(rows = seq(2, 8, 2)) # Zebra striping
)| Trial Patient Sample | |||
| First 8 patients | |||
| Chemotherapy Treatment | Age | Grade | Marker Level (ng/mL) |
|---|---|---|---|
| Drug A | 23 | II | 0.160 |
| Drug B | 9 | I | 1.107 |
| Drug A | 31 | II | 0.277 |
| Drug A | NA | III | 2.067 |
| Drug A | 51 | III | 2.767 |
| Drug B | 39 | I | 0.613 |
| Drug A | 37 | II | 0.354 |
| Drug A | 32 | I | 1.739 |
Combine group headers with zebra striping for maximum readability:
my_table <- trial |>
select(age, marker, grade, stage, trt) |>
tbl_summary(by = trt) |>
extras() |>
add_variable_group_header(
header = "Demographics",
variables = age:marker
) |>
add_variable_group_header(
header = "Disease",
variables = grade:stage
) |>
add_group_styling()
group_rows <- get_group_rows(my_table)
# Convert to gt and get the actual number of body rows
gt_table <- my_table |> as_gt()
n_body_rows <- nrow(gt_table$`_data`)
gt_table |>
gt::tab_style(
style = gt::cell_fill(color = "#E8E8E8"),
locations = gt::cells_body(rows = group_rows)
) |>
gt::tab_style(
style = gt::cell_fill(color = "#F9F9F9"),
locations = gt::cells_body(rows = !seq_len(n_body_rows) %in% group_rows)
)| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Demographics | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Disease | ||||
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
You can combine group styling with highlighting of specific variables of interest:
my_table <- trial |>
select(age, marker, grade, stage, response, trt) |>
tbl_summary(by = trt) |>
extras() |>
add_variable_group_header(
header = "Baseline",
variables = age:marker
) |>
add_variable_group_header(
header = "Disease & Outcome",
variables = grade:response
) |>
add_group_styling()
group_rows <- get_group_rows(my_table)
my_table |>
as_gt() |>
gt::tab_style(
style = gt::cell_fill(color = "#E8E8E8"),
locations = gt::cells_body(rows = group_rows)
) |>
gt::tab_style(
style = list(
gt::cell_fill(color = "#FFF9E6"),
gt::cell_text(weight = "bold")
),
locations = gt::cells_body(
columns = label,
rows = label == "Tumor Response" # Highlight primary outcome
)
)| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Baseline | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Disease & Outcome | ||||
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| Tumor Response | 61 (32%) | 28 (29%) | 33 (34%) | 0.530 |
| Unknown | 7 | 3 | 4 | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
Add subtle borders between groups for additional visual separation:
my_table <- trial |>
select(age, marker, grade, stage, trt) |>
tbl_summary(by = trt) |>
extras() |>
add_variable_group_header(
header = "Patient Factors",
variables = age:marker
) |>
add_variable_group_header(
header = "Tumor Factors",
variables = grade:stage
) |>
add_group_styling()
group_rows <- get_group_rows(my_table)
my_table |>
as_gt() |>
gt::tab_style(
style = gt::cell_fill(color = "#F5F5F5"),
locations = gt::cells_body(rows = group_rows)
) |>
gt::tab_style(
style = gt::cell_borders(
sides = "bottom",
color = "#CCCCCC",
weight = gt::px(2)
),
locations = gt::cells_body(rows = max(group_rows))
)| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| Patient Factors | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Unknown | 11 | 7 | 4 | |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| Unknown | 10 | 6 | 4 | |
| Tumor Factors | ||||
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
Here’s a comprehensive example combining all the styling techniques:
# Create dictionary for labeling (see vignette("labeling") for details)
dictionary <- tibble::tribble(
~Variable, ~Description,
"trt", "Treatment Assignment",
"age", "Age at Baseline (years)",
"marker", "Biomarker Level (ng/mL)",
"stage", "Clinical Stage",
"grade", "Tumor Grade",
"response", "Treatment Response",
"death", "Patient Died"
)
# Build the styled table
trial |>
select(trt, age, marker, grade, stage, response, death) |>
tbl_summary(by = trt, missing = "no") |>
add_auto_labels(dictionary = dictionary) |>
extras() |>
add_variable_group_header(
header = "BASELINE CHARACTERISTICS",
variables = age:marker
) |>
add_variable_group_header(
header = "DISEASE CHARACTERISTICS",
variables = grade:stage
) |>
add_variable_group_header(
header = "OUTCOMES",
variables = response:death
) |>
add_group_styling() |>
add_group_colors(color = "#E8E8E8") |>
gt::tab_header(
title = "Patient Characteristics and Outcomes by Treatment",
subtitle = "Clinical Trial Dataset"
) |>
gt::tab_source_note(
source_note = "Data from gtsummary package trial dataset"
)| Patient Characteristics and Outcomes by Treatment | ||||
| Clinical Trial Dataset | ||||
| Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 | |
|---|---|---|---|---|
| BASELINE CHARACTERISTICS | ||||
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.718 |
| Marker Level (ng/mL) | 0.64 (0.22, 1.41) | 0.84 (0.23, 1.60) | 0.52 (0.18, 1.21) | 0.085 |
| DISEASE CHARACTERISTICS | ||||
| Grade | 0.871 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| T Stage | 0.866 | |||
| T1 | 53 (27%) | 28 (29%) | 25 (25%) | |
| T2 | 54 (27%) | 25 (26%) | 29 (28%) | |
| T3 | 43 (22%) | 22 (22%) | 21 (21%) | |
| T4 | 50 (25%) | 23 (23%) | 27 (26%) | |
| OUTCOMES | ||||
| Tumor Response | 61 (32%) | 28 (29%) | 33 (34%) | 0.530 |
| Patient Died | 112 (56%) | 52 (53%) | 60 (59%) | 0.412 |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
| Data from gtsummary package trial dataset | ||||
This table demonstrates professional formatting suitable for publication:
sumExtras provides powerful tools for creating visually polished tables:
add_group_styling() - Add text
formatting (bold/italic) to group headersadd_group_colors() - Convenience
function for group colors with automatic gt conversionget_group_rows() - Identify group
header rows for further stylinguse_jama_theme() - Apply JAMA compact
theme to gtsummary tablestheme_gt_compact() - Match gt table
styling to gtsummary themeCombine these with gt’s styling functions for complete control over table appearance.
For more information:
vignette("sumExtras-intro") - Getting started with
sumExtrasvignette("labeling") - Automatic variable labeling
workflows?add_group_styling - Function documentation?add_group_colors - Function documentation?get_group_rows - Function documentation?use_jama_theme - Function documentation?theme_gt_compact - Function documentationThe styling system is designed to create professional tables efficiently. Start with simple formatting, then add visual elements as needed to enhance clarity and readability.