This guide covers common issues and their solutions when using tabviz.

Quick Diagnostic Checklist

When something isn’t working, check:

  1. Column names - Are they spelled correctly and exist in the data?
  2. Data types - Are numeric columns actually numeric?
  3. NAs - Are there unexpected missing values?
  4. Scale - Using scale = "log" with non-positive values?
  5. Dependencies - Is V8 installed for exports?

Data Issues

“Column not found in data”

Symptom: Error message about missing column.

Cause: Column name typo or the column doesn’t exist.

Solution:

Code
# Check what columns exist
names(your_data)

# Common mistake: using wrong case
forest_plot(data, point = "HR", ...)  # Wrong: "HR"
forest_plot(data, point = "hr", ...)  # Correct: "hr"

Unexpected NAs in Plot

Symptom: Rows appear without intervals or with missing values.

Cause: Columns contain NA values or wrong data types.

Solution:

Code
# Check for NAs
summary(your_data)

# Check column types - character columns won't work for numeric fields
sapply(your_data, class)

# Fix character columns that should be numeric
your_data$hr <- as.numeric(your_data$hr)

Groups Not Displaying Correctly

Symptom: Hierarchical groups appear flat or have wrong nesting.

Cause: Group column contains unexpected values or ID collisions.

Solution:

Code
# Check unique group values
table(your_data$region)
table(your_data$subregion)

# Ensure nested groups have consistent parent values
your_data |>
  group_by(region, subregion) |>
  count()

Data Rows Mixed with Headers

Symptom: Header rows show intervals, or data rows appear as headers.

Cause: row_type column has incorrect values.

Solution:

Code
# Valid row_type values
valid_types <- c("header", "data", "summary", "spacer")

# Check your values
your_data |>
  count(row_type)

# Ensure header/spacer rows have NA for estimates
your_data <- your_data |>
  mutate(
    hr = if_else(row_type %in% c("header", "spacer"), NA_real_, hr),
    lower = if_else(row_type %in% c("header", "spacer"), NA_real_, lower),
    upper = if_else(row_type %in% c("header", "spacer"), NA_real_, upper)
  )

Scale and Axis Issues

Log Scale with Non-Positive Values

Symptom: Error or unexpected axis range when using scale = "log".

Cause: Data contains zero or negative values, which are undefined on log scale.

Solution:

Code
# Find problematic rows
your_data |>
  filter(hr <= 0 | lower <= 0 | upper <= 0)

# Option 1: Filter out invalid rows
valid_data <- your_data |>
  filter(hr > 0, lower > 0, upper > 0)

# Option 2: Use linear scale instead
forest_plot(your_data, ..., scale = "linear", null_value = 0)

Axis Range Too Wide

Symptom: Data points are compressed into a small portion of the axis.

Cause: Outliers or automatic axis calculation including extreme values.

Solution:

Code
# Check data range
range(your_data$hr, na.rm = TRUE)
range(your_data$lower, na.rm = TRUE)
range(your_data$upper, na.rm = TRUE)

# Option 1: Override axis range
forest_plot(your_data, ...,
  axis_range = c(0.5, 2.0)  # Focus on relevant range
)

# Option 2: Filter outliers
filtered <- your_data |>
  filter(between(hr, 0.3, 3))

Axis Range Too Narrow

Symptom: Confidence intervals extend beyond axis bounds.

Cause: Manual axis range doesn’t accommodate all data.

Solution:

Code
# Find the actual data extent
min_val <- min(your_data$lower, na.rm = TRUE)
max_val <- max(your_data$upper, na.rm = TRUE)
message("Data range: ", round(min_val, 2), " to ", round(max_val, 2))

# Adjust axis_range to include all data
forest_plot(your_data, ...,
  axis_range = c(min_val * 0.9, max_val * 1.1)
)

Tick Marks Not Appearing

Symptom: Axis shows but has no tick marks or unexpected tick positions.

Solution:

Code
# Specify explicit tick positions
forest_plot(your_data, ...,
  scale = "log",
  axis_ticks = c(0.5, 0.7, 1, 1.5, 2)  # Explicit ticks
)

# Or use theme settings
forest_plot(your_data, ...,
  theme = web_theme_default() |>
    set_axis(tick_count = 5, gridlines = TRUE)
)

Export Failures

V8 Not Installed

Symptom: Error when calling save_plot(): “Package V8 is required”

Solution:

Code
# Install V8
install.packages("V8")

# On some systems, you may need system libraries first:
# macOS: brew install v8
# Ubuntu: sudo apt-get install libv8-dev

rsvg Issues

Symptom: PDF/PNG export fails with rsvg errors.

Solution:

Code
# Install rsvg
install.packages("rsvg")

# On macOS, you may need:
# brew install librsvg

# On Ubuntu:
# sudo apt-get install librsvg2-dev

“SVG generator JavaScript file not found”

Symptom: Export fails with message about missing JS file.

Cause: Package installation is incomplete.

Solution:

Code
# Reinstall the package
pak::pak("kaskarn/tabviz", force = TRUE)

# Verify installation
system.file("js/svg-generator.js", package = "tabviz")
# Should return a path, not ""

Colors Differ Between Display and Export

Symptom: Exported SVG/PNG has different colors than the interactive widget.

Cause: Theme not applied consistently to both.

Solution:

Code
# Use the same theme for display and export
my_theme <- web_theme_lancet()

# Interactive
forest_plot(data, ..., theme = my_theme)

# Export
spec <- web_spec(data, ..., theme = my_theme)
save_plot(spec, "output.svg")

Widget Rendering Issues

Blank Output in RStudio Viewer

Symptom: Plot doesn’t appear in RStudio’s Viewer pane.

Cause: Viewer pane size or rendering issues.

Solution:

Code
# Try in browser instead
plot <- forest_plot(your_data, ...)
htmltools::browsable(plot)

# Or save to file and open
htmlwidgets::saveWidget(plot, "temp.html")
browseURL("temp.html")

Widget Not Sizing Correctly

Symptom: Plot is too small, cut off, or doesn’t fill container.

Solution:

Code
# Specify explicit dimensions
forest_plot(your_data, ...,
  width = 900,
  height = 600
)

# For Shiny, use CSS units
forestOutput("plot", width = "100%", height = "600px")

Shiny: Proxy Not Working

Symptom: forestProxy() calls don’t update the plot.

Cause: Proxy created outside reactive context.

Solution:

Code
# Wrong: proxy at top level
# proxy <- forestProxy("forest")  # This will fail!

# Correct: proxy inside reactive context
observeEvent(input$button, {
  proxy <- forestProxy("forest")
  forest_sort(proxy, "hr", "asc")
})

Theme and Styling Issues

Styles Not Applying

Symptom: Bold, color, or other styles don’t appear.

Cause: Passing values directly instead of column names.

Solution:

Code
# Wrong: passing values directly
col_numeric("hr", bold = TRUE, color = "#ff0000")

# Correct: passing column names that contain the values
col_numeric("hr", bold = "is_bold", color = "hr_color")

# The data must have these columns:
data <- data |>
  mutate(
    is_bold = pval < 0.05,
    hr_color = if_else(hr < 1, "#16a34a", "#dc2626")
  )

Font Not Loading

Symptom: Text appears in wrong font.

Cause: Custom font not available on system.

Solution:

Code
# Use web-safe fallback fonts
web_theme_default() |>
  set_typography(
    font_family = "Georgia, 'Times New Roman', serif"
  )

# For exports, fonts must be installed on the rendering system

Debug Patterns

Inspecting WebSpec

Code
# Create spec without rendering (table-only spec)
spec <- web_spec(
  data.frame(
    study = c("A", "B"),
    hr = c(0.8, 1.2),
    n = c(100, 200)
  ),
  label = "study",
  columns = list(
    col_numeric("hr", "HR"),
    col_n("n")
  )
)

# Print summary
print(spec)
<tabviz::WebSpec>
 @ data              :'data.frame': 2 obs. of  3 variables:
 .. $ study: chr  "A" "B"
 .. $ hr   : num  0.8 1.2
 .. $ n    : num  100 200
 @ label_col         : chr "study"
 @ label_header      : chr "Study"
 @ group_col         : chr NA
 @ group_cols        : chr(0) 
 @ columns           :List of 2
 .. $ : <tabviz::ColumnSpec>
 ..  ..@ id            : chr "hr"
 ..  ..@ header        : chr "HR"
 ..  ..@ field         : chr "hr"
 ..  ..@ type          : chr "numeric"
 ..  ..@ width         : chr "auto"
 ..  ..@ align         : chr "left"
 ..  ..@ header_align  : chr "left"
 ..  ..@ wrap          : logi FALSE
 ..  ..@ sortable      : logi TRUE
 ..  ..@ options       :List of 1
 .. .. .. $ numeric:List of 4
 .. .. ..  ..$ decimals    : num 2
 .. .. ..  ..$ digits      : NULL
 .. .. ..  ..$ thousandsSep: logi FALSE
 .. .. ..  ..$ abbreviate  : logi FALSE
 ..  ..@ style_bold    : chr NA
 ..  ..@ style_italic  : chr NA
 ..  ..@ style_color   : chr NA
 ..  ..@ style_bg      : chr NA
 ..  ..@ style_badge   : chr NA
 ..  ..@ style_icon    : chr NA
 ..  ..@ style_emphasis: chr NA
 ..  ..@ style_muted   : chr NA
 ..  ..@ style_accent  : chr NA
 .. $ : <tabviz::ColumnSpec>
 ..  ..@ id            : chr "n"
 ..  ..@ header        : chr "N"
 ..  ..@ field         : chr "n"
 ..  ..@ type          : chr "numeric"
 ..  ..@ width         : chr "auto"
 ..  ..@ align         : chr "left"
 ..  ..@ header_align  : chr "left"
 ..  ..@ wrap          : logi FALSE
 ..  ..@ sortable      : logi TRUE
 ..  ..@ options       :List of 1
 .. .. .. $ numeric:List of 4
 .. .. ..  ..$ decimals    : num 0
 .. .. ..  ..$ digits      : NULL
 .. .. ..  ..$ thousandsSep: chr ","
 .. .. ..  ..$ abbreviate  : logi FALSE
 ..  ..@ style_bold    : chr NA
 ..  ..@ style_italic  : chr NA
 ..  ..@ style_color   : chr NA
 ..  ..@ style_bg      : chr NA
 ..  ..@ style_badge   : chr NA
 ..  ..@ style_icon    : chr NA
 ..  ..@ style_emphasis: chr NA
 ..  ..@ style_muted   : chr NA
 ..  ..@ style_accent  : chr NA
 @ groups            : list()
 @ summaries         : list()
 @ overall_summary   : <tabviz::GroupSummary>
 .. @ group_id: chr(0) 
 .. @ point   : int(0) 
 .. @ lower   : int(0) 
 .. @ upper   : int(0) 
 .. @ metadata: list()
 @ theme             : <tabviz::WebTheme>
 .. @ name         : chr "default"
 .. @ colors       : <tabviz::ColorPalette>
 .. .. @ background       : chr "#ffffff"
 .. .. @ foreground       : chr "#333333"
 .. .. @ primary          : chr "#0891b2"
 .. .. @ secondary        : chr "#64748b"
 .. .. @ accent           : chr "#8b5cf6"
 .. .. @ muted            : chr "#94a3b8"
 .. .. @ border           : chr "#e2e8f0"
 .. .. @ row_bg           : chr "#ffffff"
 .. .. @ alt_bg           : chr "#f8fafc"
 .. .. @ interval         : chr "#0891b2"
 .. .. @ interval_line    : chr "#475569"
 .. .. @ interval_positive: chr "#0891b2"
 .. .. @ interval_negative: chr "#dc2626"
 .. .. @ interval_neutral : chr "#64748b"
 .. .. @ summary_fill     : chr "#0891b2"
 .. .. @ summary_border   : chr "#0e7490"
 .. @ typography   : <tabviz::Typography>
 .. .. @ font_family       : chr "system-ui, -apple-system, sans-serif"
 .. .. @ font_size_sm      : chr "0.75rem"
 .. .. @ font_size_base    : chr "0.875rem"
 .. .. @ font_size_lg      : chr "1rem"
 .. .. @ font_weight_normal: num 400
 .. .. @ font_weight_medium: num 500
 .. .. @ font_weight_bold  : num 600
 .. .. @ line_height       : num 1.5
 .. .. @ header_font_scale : num 1.05
 .. @ spacing      : <tabviz::Spacing>
 .. .. @ row_height       : num 24
 .. .. @ header_height    : num 32
 .. .. @ section_gap      : num 16
 .. .. @ padding          : num 12
 .. .. @ container_padding: num 0
 .. .. @ axis_gap         : num 12
 .. .. @ group_padding    : num 8
 .. .. @ cell_padding_x   : num 10
 .. .. @ cell_padding_y   : num 4
 .. .. @ column_gap       : num 8
 .. @ shapes       : <tabviz::Shapes>
 .. .. @ point_size    : num 6
 .. .. @ summary_height: num 10
 .. .. @ line_width    : num 1.5
 .. .. @ border_radius : num 2
 .. .. @ effect_colors : chr [1:5] "#0891b2" "#16a34a" "#f59e0b" "#ef4444" "#8b5cf6"
 .. .. @ marker_shapes : chr [1:4] "square" "circle" "diamond" "triangle"
 .. @ axis         : <tabviz::AxisConfig>
 .. .. @ range_min     : num NA
 .. .. @ range_max     : num NA
 .. .. @ tick_count    : num NA
 .. .. @ tick_values   : NULL
 .. .. @ gridlines     : logi FALSE
 .. .. @ gridline_style: chr "dotted"
 .. .. @ ci_clip_factor: num 2
 .. .. @ include_null  : logi TRUE
 .. .. @ symmetric     : NULL
 .. .. @ null_tick     : logi TRUE
 .. .. @ marker_margin : logi TRUE
 .. @ layout       : <tabviz::LayoutConfig>
 .. .. @ plot_position          : chr "right"
 .. .. @ table_width            : chr "auto"
 .. .. @ plot_width             : chr "auto"
 .. .. @ container_border       : logi FALSE
 .. .. @ container_border_radius: num 8
 .. .. @ banding                : logi TRUE
 .. @ group_headers: <tabviz::GroupHeaderStyles>
 .. .. @ level1_font_size    : chr "0.9375rem"
 .. .. @ level1_font_weight  : num 600
 .. .. @ level1_italic       : logi FALSE
 .. .. @ level1_background   : NULL
 .. .. @ level1_border_bottom: logi FALSE
 .. .. @ level2_font_size    : chr "0.875rem"
 .. .. @ level2_font_weight  : num 500
 .. .. @ level2_italic       : logi FALSE
 .. .. @ level2_background   : NULL
 .. .. @ level2_border_bottom: logi FALSE
 .. .. @ level3_font_size    : chr "0.875rem"
 .. .. @ level3_font_weight  : num 400
 .. .. @ level3_italic       : logi FALSE
 .. .. @ level3_background   : NULL
 .. .. @ level3_border_bottom: logi FALSE
 .. .. @ indent_per_level    : num 16
 @ interaction       : <tabviz::InteractionSpec>
 .. @ show_filters   : logi FALSE
 .. @ show_legend    : logi TRUE
 .. @ enable_sort    : logi TRUE
 .. @ enable_collapse: logi TRUE
 .. @ enable_select  : logi TRUE
 .. @ enable_hover   : logi TRUE
 .. @ enable_resize  : logi TRUE
 .. @ enable_export  : logi TRUE
 .. @ tooltip_fields : NULL
 .. @ enable_themes  : chr "default"
 @ labels            : NULL
 @ row_bold_col      : chr NA
 @ row_italic_col    : chr NA
 @ row_color_col     : chr NA
 @ row_bg_col        : chr NA
 @ row_badge_col     : chr NA
 @ row_icon_col      : chr NA
 @ row_indent_col    : chr NA
 @ row_type_col      : chr NA
 @ row_emphasis_col  : chr NA
 @ row_muted_col     : chr NA
 @ row_accent_col    : chr NA
 @ marker_color_col  : chr NA
 @ marker_shape_col  : chr NA
 @ marker_opacity_col: chr NA
 @ marker_size_col   : chr NA
 @ weight_col        : chr NA
Code
# Access components
spec@data
  study  hr   n
1     A 0.8 100
2     B 1.2 200
Code
spec@columns
[[1]]
<tabviz::ColumnSpec>
 @ id            : chr "hr"
 @ header        : chr "HR"
 @ field         : chr "hr"
 @ type          : chr "numeric"
 @ width         : chr "auto"
 @ align         : chr "left"
 @ header_align  : chr "left"
 @ wrap          : logi FALSE
 @ sortable      : logi TRUE
 @ options       :List of 1
 .. $ numeric:List of 4
 ..  ..$ decimals    : num 2
 ..  ..$ digits      : NULL
 ..  ..$ thousandsSep: logi FALSE
 ..  ..$ abbreviate  : logi FALSE
 @ style_bold    : chr NA
 @ style_italic  : chr NA
 @ style_color   : chr NA
 @ style_bg      : chr NA
 @ style_badge   : chr NA
 @ style_icon    : chr NA
 @ style_emphasis: chr NA
 @ style_muted   : chr NA
 @ style_accent  : chr NA

[[2]]
<tabviz::ColumnSpec>
 @ id            : chr "n"
 @ header        : chr "N"
 @ field         : chr "n"
 @ type          : chr "numeric"
 @ width         : chr "auto"
 @ align         : chr "left"
 @ header_align  : chr "left"
 @ wrap          : logi FALSE
 @ sortable      : logi TRUE
 @ options       :List of 1
 .. $ numeric:List of 4
 ..  ..$ decimals    : num 0
 ..  ..$ digits      : NULL
 ..  ..$ thousandsSep: chr ","
 ..  ..$ abbreviate  : logi FALSE
 @ style_bold    : chr NA
 @ style_italic  : chr NA
 @ style_color   : chr NA
 @ style_bg      : chr NA
 @ style_badge   : chr NA
 @ style_icon    : chr NA
 @ style_emphasis: chr NA
 @ style_muted   : chr NA
 @ style_accent  : chr NA
Code
spec@theme@name
[1] "default"

Check Export Output

Code
# Generate SVG and check structure
save_plot(spec, "debug.svg")

# Read and inspect the SVG
svg_content <- readLines("debug.svg")
head(svg_content, 20)

Browser Developer Tools

For debugging interactive widgets:

  1. Right-click the widget → “Inspect”
  2. Check Console tab for JavaScript errors
  3. Check Elements tab for DOM structure
  4. Check Network tab for failed resources

Minimal Reproducible Example

When reporting issues, create a minimal example:

Code
# Minimal data that reproduces the problem
test_data <- data.frame(
  study = c("Test 1", "Test 2"),
  hr = c(0.8, 1.2),
  lower = c(0.6, 0.9),
  upper = c(1.1, 1.6)
)

# Minimal code
forest_plot(test_data,
  point = "hr", lower = "lower", upper = "upper",
  label = "study"
)

# Include session info
sessionInfo()

Getting Help

If you’ve tried the above and still have issues:

  1. Check GitHub Issues for similar problems
  2. Create a minimal reproducible example
  3. Include sessionInfo() output
  4. Open a new issue with your example and session info