Flextable for Publication-Ready Tables

Master the art of creating professional, publication-ready tables with flextable and Microsoft Word integration. From basic formatting to advanced conditional styling and automated report generation.

πŸ‘‹ Welcome to Professional Table Creation

Picture this: You've just finished analyzing your clinical trial data. The statistical models are solid, the results are exciting, and you're ready to submit to that high-impact journal. But then comes the dreaded momentβ€”creating publication-ready tables. You know the ones: perfectly formatted demographics tables, statistical results with proper significance indicators, and safety summaries that pass regulatory scrutiny. This is where flextable transforms from a helpful package into your secret weapon for professional research communication.

πŸ€” Why Does Table Quality Matter So Much?

In clinical research, tables aren't just data displaysβ€”they're the foundation of regulatory submissions, peer review, and clinical decision-making. Consider these real-world scenarios:

  • FDA Submission: Your Table 1 demographics must follow precise formatting guidelines with proper statistical annotations
  • Journal Review: Editors expect tables that are immediately interpretable without referring to lengthy footnotes
  • Clinical Guidelines: Your efficacy tables become the basis for treatment recommendations affecting thousands of patients
  • Collaborative Research: Colleagues need to easily understand and reproduce your analytical approach

Poor table formatting doesn't just look unprofessionalβ€”it can actually impede scientific communication and delay critical medical advances.

πŸ’Ž The Flextable Advantage

Flextable, created by David Gohel (the same genius behind officer), was designed specifically for one purpose: creating tables that look like they belong in prestigious journals and regulatory documents. Unlike other R table packages that focus on quick-and-dirty HTML output, flextable prioritizes professional presentation and seamless integration with Microsoft Wordβ€”still the gold standard for collaborative research documents.

🎨 Typography Control

Pixel-perfect font control, professional spacing, and regulatory-compliant formatting standards

πŸ”„ Dynamic Formatting

Conditional styling based on statistical significance, effect sizes, or custom business rules

πŸ“„ Word Integration

Perfect preservation of formatting when exported to Word documents for collaboration

πŸ“‹ Regulatory Ready

Built-in support for FDA, EMA, and ICH formatting guidelines

⚑ The "Aha!" Moment

Before flextable: "I need to create a demographics table... let me use kable(), then manually adjust the Word document formatting, then hope the journal's copyeditor doesn't mess it up, then recreate it from scratch when the reviewer asks for changes..."

With flextable: "Let me pipe my summary statistics through flextable() with professional formatting, conditional highlighting for significant results, and export directly to a Word document that looks publication-ready. When reviewers request changes, I'll just modify the R code and regenerate everything instantly."

This isn't just about saving timeβ€”it's about transforming how you think about reproducible, professional research presentation.

🧭 Your Journey Through This Tutorial

We'll build a complete clinical trial analysis report, creating four publication-ready tables that demonstrate every major flextable capability:

πŸ“‹ Table 1: Demographics
Baseline characteristics with professional typography and conditional formatting
πŸ“Š Table 2: Efficacy Results
Mixed-model results with significance indicators and confidence intervals
πŸ” Table 3: Pairwise Comparisons
Multiple comparison results with effect size interpretations
⚠️ Table 4: Safety Summary
Adverse events with regulatory-compliant formatting

By the end, you'll have not just four beautiful tables, but a complete Word document ready for submissionβ€”and the skills to create professional tables for any research project.

Ready to elevate your research presentation? Let's create tables that make reviewers and colleagues say "Wow, this looks professional!" πŸš€

🎯 Learning Objectives

By the end of this tutorial, you will:

  • Master flextable's comprehensive formatting capabilities
  • Create publication-ready demographic and statistical tables
  • Implement conditional formatting and professional styling
  • Generate automated Word documents with officer integration
  • Apply industry-standard table formatting for clinical research
  • Understand when to use flextable vs other table packages

πŸ“Š Why Flextable for Professional Tables?

🎨 Advanced Formatting

Complete control over fonts, colors, borders, and cell styling with professional typography support.

πŸ“„ Word Integration

Seamless export to Microsoft Word documents with perfect formatting preservation.

πŸ”„ Conditional Logic

Dynamic formatting based on data values with sophisticated conditional styling rules.

πŸ“‹ Clinical Standards

Built-in support for regulatory table formats including APA, FDA, and ICH guidelines.

πŸ“‹ Prerequisites: This tutorial assumes familiarity with R, dplyr, and basic statistical analysis. We'll be building complex tables from real clinical trial data with multiple statistical procedures.

πŸ› οΈ Essential Package Setup

# Core flextable ecosystem
library(flextable)      # Main table creation package
library(officer)        # Word document integration
library(gdtools)        # Font and graphics utilities

# Data manipulation and analysis
library(dplyr)          # Data wrangling
library(broom)          # Statistical model tidying
library(emmeans)        # Marginal means and contrasts
library(rstatix)        # Statistical tests and effect sizes

# Specialized formatting
library(scales)         # Number and label formatting
library(stringr)        # String manipulation for labels
πŸ’‘ Pro Tip: Install the full flextable ecosystem with install.packages(c("flextable", "officer", "gdtools")) for complete functionality.

πŸ“‹ Table 1: Demographics and Baseline Characteristics

Creating Professional Demographics Tables

# Generate realistic clinical trial dataset
set.seed(123)
n_per_group <- 60
total_n <- n_per_group * 4

clinical_data <- data.frame(
  patient_id = 1:total_n,
  treatment = rep(c("Placebo", "Low Dose", "High Dose", "Combination"), each = n_per_group),
  age = c(
    rnorm(n_per_group, 65, 8),    # Placebo
    rnorm(n_per_group, 63, 9),    # Low Dose  
    rnorm(n_per_group, 66, 7),    # High Dose
    rnorm(n_per_group, 64, 8)     # Combination
  ),
  gender = sample(c("Male", "Female"), total_n, replace = TRUE, prob = c(0.6, 0.4)),
  baseline_sbp = rnorm(total_n, 145, 15),
  baseline_dbp = rnorm(total_n, 85, 10),
  diabetes = sample(c("Yes", "No"), total_n, replace = TRUE, prob = c(0.3, 0.7)),
  smoking = sample(c("Never", "Former", "Current"), total_n, replace = TRUE, prob = c(0.4, 0.4, 0.2))
)

Advanced Demographics Summary with Statistical Testing

# Create comprehensive demographics table
table1_data <- clinical_data %>%
  group_by(treatment) %>%
  summarise(
    n = n(),
    
    # Age statistics
    age_mean = round(mean(age), 1),
    age_sd = round(sd(age), 1),
    age_summary = paste0(age_mean, " (", age_sd, ")"),
    
    # Gender distribution
    male_n = sum(gender == "Male"),
    male_pct = round(100 * male_n / n(), 1),
    female_n = sum(gender == "Female"), 
    female_pct = round(100 * female_n / n(), 1),
    
    # Baseline blood pressure
    sbp_mean = round(mean(baseline_sbp), 1),
    sbp_sd = round(sd(baseline_sbp), 1),
    sbp_summary = paste0(sbp_mean, " (", sbp_sd, ")"),
    
    dbp_mean = round(mean(baseline_dbp), 1),
    dbp_sd = round(sd(baseline_dbp), 1),
    dbp_summary = paste0(dbp_mean, " (", dbp_sd, ")"),
    
    # Comorbidities
    diabetes_n = sum(diabetes == "Yes"),
    diabetes_pct = round(100 * diabetes_n / n(), 1),
    diabetes_summary = paste0(diabetes_n, " (", diabetes_pct, "%)"),
    
    # Smoking status
    never_smoker_n = sum(smoking == "Never"),
    never_smoker_pct = round(100 * never_smoker_n / n(), 1),
    former_smoker_n = sum(smoking == "Former"),
    former_smoker_pct = round(100 * former_smoker_n / n(), 1),
    current_smoker_n = sum(smoking == "Current"),
    current_smoker_pct = round(100 * current_smoker_n / n(), 1)
  ) %>%
  ungroup()
Professional Demographics Table

Table 1: Baseline demographics with professional formatting and statistical annotations

🎨 Advanced Flextable Formatting

Professional Typography and Layout

# Create publication-ready table with advanced formatting
table1_flex <- table1_data %>%
  select(treatment, age_summary, male_n, female_n, sbp_summary, 
         dbp_summary, diabetes_summary) %>%
  flextable() %>%
  
  # Set professional headers
  set_header_labels(
    treatment = "Treatment Group",
    age_summary = "Age, years\nMean (SD)",
    male_n = "Male\nn (%)",
    female_n = "Female\nn (%)", 
    sbp_summary = "Systolic BP, mmHg\nMean (SD)",
    dbp_summary = "Diastolic BP, mmHg\nMean (SD)",
    diabetes_summary = "Diabetes Mellitus\nn (%)"
  ) %>%
  
  # Apply professional theme
  theme_vanilla() %>%
  
  # Typography specifications
  fontsize(size = 10, part = "all") %>%
  font(fontname = "Times New Roman", part = "all") %>%
  
  # Header formatting
  bold(part = "header") %>%
  align(align = "center", part = "header") %>%
  bg(bg = "#f8f9fa", part = "header") %>%
  
  # Body alignment
  align(j = 1, align = "left", part = "body") %>%
  align(j = 2:7, align = "center", part = "body") %>%
  
  # Conditional formatting for treatment groups
  bg(i = ~ treatment == "Placebo", bg = "#fff3cd") %>%
  bg(i = ~ treatment == "Combination", bg = "#d1ecf1") %>%
  
  # Border specifications
  border_outer(border = fp_border_default(color = "#000000", width = 2)) %>%
  border_inner_h(border = fp_border_default(color = "#cccccc", width = 1)) %>%
  border_inner_v(border = fp_border_default(color = "#cccccc", width = 1)) %>%
  
  # Table width optimization
  width(width = c(1.8, 1.2, 1, 1, 1.3, 1.3, 1.2)) %>%
  
  # Add footnotes
  add_footer_lines("Data presented as mean (standard deviation) or n (%). BP = Blood Pressure.") %>%
  fontsize(size = 8, part = "footer") %>%
  italic(part = "footer")
βœ… Formatting Best Practices:
  • Typography: Use Times New Roman 10pt for body, 8pt for footnotes
  • Alignment: Left-align categories, center-align data
  • Colors: Subtle backgrounds for grouping, avoid bright colors
  • Borders: Clear outer borders, subtle inner gridlines

πŸ“Š Statistical Results Tables

Efficacy Analysis with Mixed Models

# Generate follow-up efficacy data
clinical_data <- clinical_data %>%
  mutate(
    # Treatment effects with realistic clinical patterns
    treatment_effect = case_when(
      treatment == "Placebo" ~ 0,
      treatment == "Low Dose" ~ -8,
      treatment == "High Dose" ~ -15,
      treatment == "Combination" ~ -22
    ),
    
    # Endpoint with treatment effect and individual variation
    endpoint_reduction = treatment_effect + rnorm(n(), 0, 12),
    endpoint_reduction = pmax(endpoint_reduction, -50) # Clinical floor effect
  )

# Fit mixed-effects model for efficacy analysis  
library(glmmTMB)
efficacy_model <- glmmTMB(
  endpoint_reduction ~ treatment + age + gender + baseline_sbp + (1|patient_id),
  data = clinical_data,
  family = gaussian()
)

# Extract and format model results
efficacy_results <- broom.mixed::tidy(efficacy_model, conf.int = TRUE) %>%
  filter(effect == "fixed", term != "(Intercept)") %>%
  mutate(
    term = case_when(
      term == "treatmentLow Dose" ~ "Low Dose vs Placebo",
      term == "treatmentHigh Dose" ~ "High Dose vs Placebo", 
      term == "treatmentCombination" ~ "Combination vs Placebo",
      term == "age" ~ "Age (per year)",
      term == "genderMale" ~ "Male vs Female",
      term == "baseline_sbp" ~ "Baseline SBP (per mmHg)",
      TRUE ~ term
    ),
    
    # Format estimates and confidence intervals
    estimate_formatted = paste0(
      sprintf("%.2f", estimate), 
      " (", sprintf("%.2f", conf.low), 
      ", ", sprintf("%.2f", conf.high), ")"
    ),
    
    # Format p-values
    p_formatted = case_when(
      p.value < 0.001 ~ "<0.001",
      p.value < 0.01 ~ sprintf("%.3f", p.value),
      TRUE ~ sprintf("%.2f", p.value)
    ),
    
    # Statistical significance indicators
    significance = case_when(
      p.value < 0.001 ~ "***",
      p.value < 0.01 ~ "**", 
      p.value < 0.05 ~ "*",
      TRUE ~ ""
    )
  )

Professional Efficacy Results Table

# Create comprehensive efficacy results table
efficacy_flex <- efficacy_results %>%
  select(term, estimate_formatted, p_formatted, significance) %>%
  flextable() %>%
  
  # Professional headers
  set_header_labels(
    term = "Parameter",
    estimate_formatted = "Estimate (95% CI)",
    p_formatted = "P-value", 
    significance = "Sig."
  ) %>%
  
  # Apply clinical trial table theme
  theme_booktabs() %>%
  
  # Typography
  fontsize(size = 11, part = "all") %>%
  font(fontname = "Times New Roman", part = "all") %>%
  
  # Header styling
  bold(part = "header") %>%
  align(align = "center", part = "header") %>%
  bg(bg = "#2c5aa0", part = "header") %>%
  color(color = "white", part = "header") %>%
  
  # Body alignment and formatting
  align(j = 1, align = "left", part = "body") %>%
  align(j = 2:4, align = "center", part = "body") %>%
  
  # Conditional formatting for significance
  bg(i = ~ significance == "***", j = 4, bg = "#28a745") %>%
  bg(i = ~ significance == "**", j = 4, bg = "#ffc107") %>%
  bg(i = ~ significance == "*", j = 4, bg = "#fd7e14") %>%
  color(i = ~ significance %in% c("***", "**", "*"), j = 4, color = "white") %>%
  bold(i = ~ significance != "", j = 1:4) %>%
  
  # Professional borders
  border_outer(border = fp_border_default(color = "#2c5aa0", width = 2)) %>%
  hline_top(border = fp_border_default(color = "#2c5aa0", width = 2), part = "header") %>%
  hline_bottom(border = fp_border_default(color = "#2c5aa0", width = 2), part = "body") %>%
  
  # Optimized column widths
  width(width = c(2.5, 2, 1, 0.8)) %>%
  
  # Comprehensive footnotes
  add_footer_lines(c(
    "CI = Confidence Interval. Model: Mixed-effects regression adjusting for age, gender, and baseline SBP.",
    "Significance: *** p<0.001, ** p<0.01, * p<0.05"
  )) %>%
  fontsize(size = 9, part = "footer") %>%
  italic(part = "footer")
Efficacy Results Table

Table 2: Treatment efficacy results with conditional significance formatting

πŸ” Advanced Pairwise Comparisons

Comprehensive Multiple Comparisons with emmeans

# Perform comprehensive pairwise comparisons
treatment_emmeans <- emmeans(efficacy_model, "treatment")

# All pairwise comparisons with multiplicity adjustment
pairwise_results <- pairs(treatment_emmeans, adjust = "tukey") %>%
  broom::tidy(conf.int = TRUE) %>%
  mutate(
    # Clean comparison labels
    contrast_clean = str_replace_all(contrast, " - ", " vs "),
    
    # Format estimates with confidence intervals
    estimate_ci = paste0(
      sprintf("%.2f", estimate),
      " (", sprintf("%.2f", conf.low), 
      ", ", sprintf("%.2f", conf.high), ")"
    ),
    
    # Effect size interpretation
    effect_size = case_when(
      abs(estimate) < 5 ~ "Small",
      abs(estimate) < 15 ~ "Moderate", 
      abs(estimate) < 25 ~ "Large",
      TRUE ~ "Very Large"
    ),
    
    # Clinical significance
    clinical_significance = case_when(
      abs(estimate) >= 10 & adj.p.value < 0.05 ~ "Clinically Significant",
      abs(estimate) >= 10 & adj.p.value >= 0.05 ~ "Clinically Relevant",
      abs(estimate) < 10 & adj.p.value < 0.05 ~ "Statistically Significant",
      TRUE ~ "Not Significant"
    ),
    
    # Format adjusted p-values
    p_adj_formatted = case_when(
      adj.p.value < 0.001 ~ "<0.001",
      adj.p.value < 0.01 ~ sprintf("%.3f", adj.p.value),
      TRUE ~ sprintf("%.3f", adj.p.value)
    )
  )

Professional Pairwise Comparisons Table

# Create advanced pairwise comparisons table
pairwise_flex <- pairwise_results %>%
  select(contrast_clean, estimate_ci, p_adj_formatted, effect_size, clinical_significance) %>%
  flextable() %>%
  
  # Professional headers with line breaks
  set_header_labels(
    contrast_clean = "Treatment\nComparison",
    estimate_ci = "Difference in Reduction\n(95% CI)",
    p_adj_formatted = "Adjusted\nP-value",
    effect_size = "Effect\nSize",
    clinical_significance = "Clinical\nInterpretation"
  ) %>%
  
  # Apply sophisticated theme
  theme_zebra() %>%
  
  # Typography specifications
  fontsize(size = 10, part = "all") %>%
  font(fontname = "Arial", part = "all") %>%
  
  # Header styling
  bold(part = "header") %>%
  align(align = "center", part = "header") %>%
  bg(bg = "#34495e", part = "header") %>%
  color(color = "white", part = "header") %>%
  
  # Body alignment
  align(j = 1, align = "left", part = "body") %>%
  align(j = 2:5, align = "center", part = "body") %>%
  
  # Conditional formatting for clinical significance
  bg(i = ~ clinical_significance == "Clinically Significant", bg = "#d4edda") %>%
  bg(i = ~ clinical_significance == "Clinically Relevant", bg = "#fff3cd") %>%
  bg(i = ~ clinical_significance == "Statistically Significant", bg = "#cce5ff") %>%
  bg(i = ~ clinical_significance == "Not Significant", bg = "#f8d7da") %>%
  
  # Effect size formatting
  bold(i = ~ effect_size %in% c("Large", "Very Large"), j = 4) %>%
  color(i = ~ effect_size == "Very Large", j = 4, color = "#dc3545") %>%
  color(i = ~ effect_size == "Large", j = 4, color = "#fd7e14") %>%
  
  # Professional borders
  border_outer(border = fp_border_default(color = "#34495e", width = 2)) %>%
  hline(i = c(3, 6), border = fp_border_default(color = "#95a5a6", width = 1)) %>%
  
  # Optimized column widths
  width(width = c(2.2, 2.2, 1.2, 1, 1.8)) %>%
  
  # Detailed footnotes
  add_footer_lines(c(
    "All pairwise comparisons adjusted using Tukey's method for multiple comparisons.",
    "Clinical significance defined as |difference| β‰₯10 mmHg reduction with p<0.05.",
    "Effect sizes: Small <5, Moderate 5-15, Large 15-25, Very Large >25 mmHg."
  )) %>%
  fontsize(size = 8, part = "footer") %>%
  italic(part = "footer")
Pairwise Comparisons Table

Table 3: Comprehensive pairwise treatment comparisons with clinical interpretation

πŸ“„ Microsoft Word Integration

Automated Report Generation with officer

# Create comprehensive Word document with multiple tables
library(officer)

# Initialize Word document with professional template
doc <- read_docx() %>%
  
  # Document title and metadata
  body_add_par("Clinical Trial Statistical Analysis Report", 
               style = "heading 1") %>%
  body_add_par(paste("Generated on:", Sys.Date()), 
               style = "Normal") %>%
  body_add_break() %>%
  
  # Executive summary
  body_add_par("Executive Summary", style = "heading 2") %>%
  body_add_par(paste(
    "This report presents comprehensive statistical analyses of a",
    "randomized controlled trial comparing four treatment arms:",
    "placebo, low dose, high dose, and combination therapy.",
    "The primary endpoint was reduction in systolic blood pressure",
    "from baseline to week 12."
  ), style = "Normal") %>%
  body_add_break() %>%
  
  # Table 1: Demographics
  body_add_par("Table 1: Baseline Demographics and Characteristics", 
               style = "heading 2") %>%
  body_add_flextable(table1_flex) %>%
  body_add_break() %>%
  
  # Table 2: Efficacy Results  
  body_add_par("Table 2: Primary Efficacy Analysis", 
               style = "heading 2") %>%
  body_add_flextable(efficacy_flex) %>%
  body_add_break() %>%
  
  # Table 3: Pairwise Comparisons
  body_add_par("Table 3: Pairwise Treatment Comparisons", 
               style = "heading 2") %>%
  body_add_flextable(pairwise_flex) %>%
  body_add_break() %>%
  
  # Statistical methods section
  body_add_par("Statistical Methods", style = "heading 2") %>%
  body_add_par(paste(
    "Primary efficacy analysis was conducted using a mixed-effects",
    "linear regression model with treatment as fixed effect and",
    "patient as random effect. Covariates included age, gender,",
    "and baseline systolic blood pressure. Pairwise comparisons",
    "between treatment groups were performed using estimated", 
    "marginal means with Tukey adjustment for multiple comparisons.",
    "All analyses were performed using R statistical software",
    "with significance level set at Ξ± = 0.05."
  ), style = "Normal")

# Save the comprehensive report
print(doc, target = "flextable_examples/comprehensive_clinical_report.docx")
βœ… Word Integration Benefits:
  • Perfect Formatting: Tables maintain all formatting in Word
  • Professional Layout: Automated page breaks and section headers
  • Collaborative Workflow: Easy sharing and commenting in Word
  • Regulatory Compliance: Standard format for FDA submissions

βš™οΈ Advanced Flextable Features

🎨 Styling Features

  • Themes: vanilla, booktabs, zebra, box
  • Fonts: Any system font with size control
  • Colors: Background, text, and border colors
  • Alignment: Horizontal and vertical alignment
  • Borders: Custom border styles and widths

πŸ“Š Data Features

  • Conditional Formatting: Data-driven styling
  • Merging: Cell and column merging
  • Headers/Footers: Multi-line annotations
  • Grouping: Hierarchical data presentation
  • Pagination: Automatic page breaks

Safety and Adverse Events Table

# Generate safety data for demonstration
set.seed(456)  # Different seed for safety data
safety_data <- clinical_data %>%
  rowwise() %>%
  mutate(
    # Simulate adverse events
    any_ae = sample(c("Yes", "No"), 1, prob = c(0.4, 0.6)),
    
    # Serious adverse events (only if any AE)
    serious_ae = ifelse(any_ae == "Yes", 
                       sample(c("Yes", "No"), 1, prob = c(0.1, 0.9)),
                       "No"),
    
    # Treatment-related AEs (varies by treatment)
    tr_ae_prob = case_when(
      treatment == "Placebo" ~ 0.2,
      treatment == "Low Dose" ~ 0.3,
      treatment == "High Dose" ~ 0.5,
      treatment == "Combination" ~ 0.6
    ),
    treatment_related_ae = ifelse(any_ae == "Yes",
                                 sample(c("Yes", "No"), 1, prob = c(tr_ae_prob, 1 - tr_ae_prob)),
                                 "No"),
    
    # Discontinuations due to AEs
    discontinuation = sample(c("Yes", "No"), 1, prob = c(0.05, 0.95))
  ) %>%
  ungroup() %>%
  select(-tr_ae_prob)  # Remove helper column

# Create safety summary table
safety_summary <- safety_data %>%
  group_by(treatment) %>%
  summarise(
    n = n(),
    
    # Any adverse event
    any_ae_n = sum(any_ae == "Yes"),
    any_ae_pct = round(100 * any_ae_n / n(), 1),
    any_ae_summary = paste0(any_ae_n, " (", any_ae_pct, "%)"),
    
    # Serious adverse events
    serious_ae_n = sum(serious_ae == "Yes"),
    serious_ae_pct = round(100 * serious_ae_n / n(), 1),
    serious_ae_summary = paste0(serious_ae_n, " (", serious_ae_pct, "%)"),
    
    # Treatment-related AEs
    tr_ae_n = sum(treatment_related_ae == "Yes"),
    tr_ae_pct = round(100 * tr_ae_n / n(), 1),
    tr_ae_summary = paste0(tr_ae_n, " (", tr_ae_pct, "%)"),
    
    # Discontinuations
    disc_n = sum(discontinuation == "Yes"),
    disc_pct = round(100 * disc_n / n(), 1),
    disc_summary = paste0(disc_n, " (", disc_pct, "%)")
  ) %>%
  ungroup()

# Create professional safety table
safety_flex <- safety_summary %>%
  select(treatment, any_ae_summary, serious_ae_summary, 
         tr_ae_summary, disc_summary) %>%
  flextable() %>%
  
  # Professional headers
  set_header_labels(
    treatment = "Treatment\nGroup",
    any_ae_summary = "Any Adverse\nEvent\nn (%)",
    serious_ae_summary = "Serious Adverse\nEvent\nn (%)",
    tr_ae_summary = "Treatment-Related\nAdverse Event\nn (%)",
    disc_summary = "Discontinuation\ndue to AE\nn (%)"
  ) %>%
  
  # Apply regulatory theme
  theme_box() %>%
  
  # Typography
  fontsize(size = 9, part = "all") %>%
  font(fontname = "Calibri", part = "all") %>%
  
  # Header styling
  bold(part = "header") %>%
  align(align = "center", part = "header") %>%
  bg(bg = "#8b0000", part = "header") %>%
  color(color = "white", part = "header") %>%
  
  # Body formatting
  align(j = 1, align = "left", part = "body") %>%
  align(j = 2:5, align = "center", part = "body") %>%
  
  # Conditional formatting for safety signals
  bg(i = ~ treatment == "High Dose", bg = "#ffe6e6") %>%
  bg(i = ~ treatment == "Combination", bg = "#ffcccc") %>%
  
  # Professional borders
  border_outer(border = fp_border_default(color = "#8b0000", width = 2)) %>%
  
  # Column width optimization
  width(width = c(1.5, 1.3, 1.3, 1.5, 1.3)) %>%
  
  # Safety footnotes
  add_footer_lines(c(
    "AE = Adverse Event. Safety population includes all randomized patients who received β‰₯1 dose.",
    "Treatment-related AEs determined by investigator assessment."
  )) %>%
  fontsize(size = 8, part = "footer") %>%
  italic(part = "footer")
Safety Summary Table

Table 4: Safety and tolerability summary with regulatory formatting

πŸ“ˆ When to Use Flextable vs Alternatives

βœ… Use Flextable When:

  • Creating publication-ready tables
  • Need Word document integration
  • Require advanced conditional formatting
  • Working with regulatory submissions
  • Need professional typography control
  • Creating complex multi-part tables

❌ Consider Alternatives When:

  • kable/kableExtra: Simple HTML tables
  • DT: Interactive web tables
  • gt: Grammar of tables approach
  • huxtable: LaTeX integration needed
  • reactable: JavaScript interactivity
  • formattable: Quick data.frame styling
⚑ Performance Considerations: Flextable can be slower for very large tables (>1000 rows). For massive datasets, consider using DT or reactable for web output, or pre-aggregate data before flextable formatting.

🎯 Advanced Tips and Best Practices

Professional Table Design Principles

1. Typography Hierarchy:

  • Headers: Bold, 10-12pt, centered
  • Body text: Regular, 9-10pt, data-appropriate alignment
  • Footnotes: Italic, 8pt, left-aligned

2. Color Strategy:

  • Headers: Dark professional colors (#2c5aa0, #34495e)
  • Alternating rows: Very light grays (#f8f9fa)
  • Conditional highlighting: Meaningful, accessible colors

3. Layout Guidelines:

  • Left-align text categories, center-align numeric data
  • Use consistent decimal places within columns
  • Group related columns with subtle visual cues
  • Provide clear, informative headers with units
# Template for professional table creation
create_professional_table <- function(data, title = NULL) {
  data %>%
    flextable() %>%
    
    # Apply consistent theme
    theme_vanilla() %>%
    
    # Standard typography
    fontsize(size = 10, part = "all") %>%
    font(fontname = "Times New Roman", part = "all") %>%
    
    # Header formatting
    bold(part = "header") %>%
    align(align = "center", part = "header") %>%
    bg(bg = "#2c5aa0", part = "header") %>%
    color(color = "white", part = "header") %>%
    
    # Professional borders
    border_outer(border = fp_border_default(color = "#2c5aa0", width = 2)) %>%
    border_inner_h(border = fp_border_default(color = "#cccccc", width = 1)) %>%
    
    # Auto-fit width
    autofit() %>%
    
    # Add title if provided
    {if (!is.null(title)) add_header_lines(., title) else .} %>%
    {if (!is.null(title)) bold(., part = "header") else .} %>%
    {if (!is.null(title)) fontsize(., size = 12, part = "header") else .}
}

πŸš€ Next Steps and Resources

What You've Accomplished

  • βœ… Mastered flextable's comprehensive formatting system
  • βœ… Created publication-ready demographic and statistical tables
  • βœ… Implemented advanced conditional formatting
  • βœ… Generated automated Word reports with officer
  • βœ… Applied industry-standard clinical table formatting
  • βœ… Learned when to use flextable vs alternative packages

🎯 Practice Exercises

  • Create custom themes for your organization
  • Build automated reporting workflows
  • Implement accessibility-compliant tables
  • Design multi-language table templates