rguides

Interactive ggplot with ggiraph

Interactive ggplot with ggiraph

Add tooltips, hover effects, and click selection to your ggplot2 plots with ggiraph, an htmlwidget that makes static R graphics interactive and exportable as self-contained HTML.

What is ggiraph?

ggiraph is an R package by David Gohel that converts ggplot2 plots into interactive HTML/SVG graphics. Because it is built as an htmlwidget, it works in R Markdown documents, Shiny apps, and standalone HTML files without extra configuration.

Install from CRAN:

install.packages("ggiraph")

ggraph depends on ggplot2 for the plotting grammar and systemfonts for rendering text in SVG output. Every ggiraph session starts by loading both libraries; ggplot2 to construct the plot, and ggiraph to add the interactive layer on top. Loading them together is the standard starting point for any interactive graphic:

library(ggplot2)
library(ggiraph)

The pattern is straightforward: replace standard ggplot2 geoms with their geom_X_interactive() counterparts, supply a tooltip aesthetic for hover text and a data_id aesthetic to identify individual elements for CSS targeting, then wrap the entire plot object with girafe() to activate the interactivity. The rendering step is what transforms a static ggplot into an interactive SVG; without girafe(), the interactive geoms render like regular geoms.

Your first interactive plot

Here is a minimal example with the mtcars dataset:

dataset <- mtcars
dataset$carname <- row.names(mtcars)

p <- ggplot(
  data = dataset,
  mapping = aes(
    x = wt,
    y = qsec,
    tooltip = carname,
    data_id = carname
  )
) +
  geom_point_interactive(size = 3) +
  labs(x = "Weight (1000 lbs)", y = "Quarter mile time (s)")

x <- girafe(ggobj = p)
x

Hover over any point to see the car name. The data_id aesthetic is also set, it enables CSS-based hover effects and, in Shiny, the selection reactive.

The three-step pattern for any interactive plot:

  1. Add tooltip and data_id aesthetics inside aes()
  2. Use geom_X_interactive() instead of geom_X()
  3. Wrap the plot with girafe()

Building multi-line tooltips

The tooltip aesthetic accepts a character string. Use paste() to combine multiple data columns:

# Prepare the dataset
dataset <- mtcars
dataset$carname <- row.names(mtcars)
dataset$label <- paste0(
  dataset$carname, "\n",
  "Weight: ", round(dataset$wt, 2), " | ",
  "Qsec: ", round(dataset$qsec, 2)
)

p <- ggplot(dataset, aes(wt, qsec, tooltip = label, data_id = carname)) +
  geom_point_interactive(size = 3)

girafe(ggobj = p)

The tooltip now shows the car name, weight, and quarter-mile time on three lines. Note that paste0() coerces numbers to character automatically, no as.character() call needed for numeric columns.

Bar charts with geom_bar_interactive

Bar charts follow the same pattern. Remember to set stat = "identity" when mapping y to a column directly:

dat <- data.frame(
  name = c("Guy", "Ginette", "David", "Cedric", "Frederic"),
  height = c(169, 160, 171, 172, 171)
)

p <- ggplot(dat, aes(x = name, y = height, fill = name, data_id = name)) +
  geom_bar_interactive(stat = "identity") +
  labs(x = NULL, y = "Height (cm)") +
  theme_minimal() +
  theme(legend.position = "none")

girafe(ggobj = p)

Customising hover and selection CSS

With data_id set on each geom, you can control the visual feedback users receive when they hover or click. The opts_hover() and opts_selection() functions accept raw CSS strings that are applied directly to the SVG elements, giving you full control over fill colors, stroke widths, opacity, and cursor styles:

hover_css <- "fill: #ffe7a6; fill-opacity: 0.8; cursor: pointer;"
selected_css <- "fill: #ff6b6b; stroke: #333; stroke-width: 2px;"

p <- ggplot(mtcars, aes(wt, mpg, tooltip = rownames(mtcars), data_id = rownames(mtcars))) +
  geom_point_interactive(size = 3) +
  theme_minimal()

x <- girafe(ggobj = p) %>%
  girafe_options(
    opts_hover(css = hover_css),
    opts_selection(css = selected_css)
  )

x

Without data_id, the hover and selection CSS has no elements to target, the browser needs a unique identifier for each geom element to apply the styles.

You can also construct CSS strings programmatically with girafe_css(). This is most useful in Shiny apps where you want to generate styles from server-side data:

my_css <- girafe_css(
  text = "font-style: italic;",
  id = "none"
)
# Returns: "text{fill:inherit;stroke:inherit;font-style:italic;} "

Styling the tooltip box

The default tooltip appearance is functional but visually plain. Use opts_tooltip() to control the tooltip container’s styling, background color, font, padding, and border radius, via a CSS string. This is especially useful when integrating ggiraph plots into branded reports or dashboards where the tooltip should match the surrounding color scheme:

tooltip_css <- "background-color: #1a1a2e; color: #fff;
                padding: 6px; border-radius: 4px; font-size: 12px;"

x <- girafe(ggobj = p) %>%
  girafe_options(
    opts_tooltip(css = tooltip_css, use_fill = TRUE)
  )

x

Setting use_fill = TRUE applies the background-color from your CSS to the fill rather than the stroke. The tooltip box offsets from the cursor are configurable but default to sensible values.

Controlling selection behaviour

The opts_selection() function controls what happens when a user clicks an element:

x <- girafe(ggobj = p) %>%
  girafe_options(
    opts_selection(
      type = "single",       # "single" or "multiple"
      only_shiny = FALSE,   # TRUE restricts selection to Shiny only
      css = "fill: #ff6b6b;"
    )
  )

x
  • type = "single", only one element selected at a time
  • type = "multiple", Ctrl/Cmd+click to select several
  • only_shiny = FALSE, lets you preview the selection highlight in a plain HTML file, not just in Shiny

Sizing the output with opts_sizing

ggiraph renders SVG graphics. By default it rescales the output to fit the container. Control this with opts_sizing():

x <- girafe(ggobj = p, width_svg = 10, height_svg = 6) %>%
  girafe_options(
    opts_sizing(rescale = TRUE, width = 0.9)
  )

x
  • width_svg and height_svg in girafe() set the SVG canvas dimensions in inches
  • opts_sizing(rescale = TRUE) scales that SVG to fit the container
  • opts_sizing(rescale = FALSE) displays it at exact pixel size

Using ggiraph with Shiny

Selection is most powerful in Shiny, where the selected data_id values feed into reactive expressions:

# ui.R
girafeOutput("scatter_plot")

# server.R
output$scatter_plot <- renderGirafe({
  girafe(
    ggobj = ggplot(mtcars, aes(wt, mpg, tooltip = rownames(mtcars), data_id = rownames(mtcars))) +
      geom_point_interactive(size = 3),
    options = list(
      opts_selection(type = "single", only_shiny = TRUE)
    )
  )
})

With only_shiny = TRUE, the selection is stored as a Shiny reactive value accessible via input$scatter_plot_selected. You can use this to filter data tables, update other outputs, or drive conditional logic elsewhere in the app.

Available interactive geoms

Most ggplot2 geoms have an interactive counterpart. Key ones:

ggplot2 geomggiraph geom
geom_point()geom_point_interactive()
geom_line()geom_line_interactive()
geom_area()geom_area_interactive()
geom_bar()geom_bar_interactive()
geom_label()geom_label_interactive()
geom_text()geom_text_interactive()
geom_smooth()geom_smooth_interactive()
geom_ribbon()geom_ribbon_interactive()
geom_tile()geom_tile_interactive()
geom_sf()geom_sf_interactive()

If a geom has an *_interactive() version documented at davidgohel.github.io/ggiraph, it is supported.

Tooltips and hover behavior

ggiraph tooltips accept HTML. tooltip = paste0("<b>", name, "</b><br>Value: ", value) creates a formatted tooltip with a bold title and a value line. CSS styling in the tooltip HTML is rendered in the browser, allowing colored text, images, and tables within tooltips. The tooltip_css argument in girafe() applies CSS to all tooltips in the chart.

Hover behavior (hover_css) highlights elements on mouse-over. hover_css = "fill:orange;stroke:black" colors hovered elements orange with a black border. selected_css styles elements that are clicked and selected (for multi-select interactions).

The tooltip aesthetic is an HTML string. Any HTML that renders in a browser works: formatted text, bold labels, line breaks, even inline SVG or images. A tooltip that shows the formatted values of relevant data columns — “Region: Northeast Sales: $1.2M” — answers the reader’s likely hover question immediately.

Girafe options control tooltip behavior: hover_css changes the cursor appearance and element styling on hover, tooltip_css controls the tooltip box appearance, and tooltip_delay sets the delay before tooltips appear. Consistent tooltip styling across multiple ggiraph charts in the same report creates a professional, unified look. Setting the CSS options once in a shared girafe_options object and applying it to all charts maintains consistency with minimal repetition.

Linking multiple plots

ggiraph’s data_id aesthetic enables linked highlighting across multiple charts in a single Shiny app. When the user hovers over an element in one chart, elements with the same data_id in other charts are highlighted simultaneously. Set the same data_id value across charts: geom_point_interactive(aes(data_id = id_column)) in both charts. Use girafe(ggobj = p, options = list(opts_selection(type = "multiple"))) to enable multi-element selection and pass selected IDs between charts via input$chart_selected.

For Shiny integration, girafeOutput() and renderGirafe() work like other Shiny output functions. The selected data IDs from user interaction are available as input$chart_selected, enabling downstream filtering and plot updates based on what the user clicked.

Adding interactivity to existing plots

The fastest path to an interactive ggplot2 chart is replacing static geoms with their ggiraph counterparts: geom_point_interactive() instead of geom_point(), geom_bar_interactive() instead of geom_bar(). The interactive geoms accept tooltip, data_id, and onclick aesthetics. Everything else in the ggplot2 code, scales, themes, facets, remains unchanged.

CSS customization

ggiraph hover and selection styles are controlled with CSS class names, not R code. opts_hover() and opts_selection() accept CSS strings. .hover_css sets the style for hovered elements; .selected_css sets the style for selected elements. Use opacity changes for subtle effects: "opacity:0.8;" dims non-hovered elements when a hover CSS is set on hovered ones. scale_color_interactive() and scale_fill_interactive() make legend items interactive — hovering over a legend entry highlights all corresponding chart elements.

How ggiraph fits into ggplot2

ggiraph converts ggplot2 charts to interactive SVG. Instead of replacing the ggplot2 workflow, ggiraph extends it with interactive geom variants: geom_point_interactive, geom_bar_interactive, geom_col_interactive, and others. Each interactive geom accepts tooltip, data_id, and onclick aesthetic mappings. The tooltip controls what appears when hovering; data_id enables cross-highlighting between multiple charts; onclick executes JavaScript.

The workflow is: build the ggplot2 chart using interactive geoms, then render it with girafe(). The result is an htmlwidget that works in R Markdown, Quarto, and Shiny. The final output is SVG with embedded JavaScript that handles the hover, click, and selection behavior. Because it builds on ggplot2, every ggplot2 feature — faceting, themes, scales, coordinate systems — works with ggiraph interactive geoms.

Cross-Highlighting between charts

The data_id aesthetic enables cross-chart selection. When a data point in one chart is clicked or hovered, all elements sharing the same data_id in any chart on the page are highlighted simultaneously. This creates a linked selection experience: hovering a country in a map highlights that country in the accompanying bar chart and time series. The data_id values are matched as strings across all girafe charts on the page.

In Shiny, ggiraph integrates through inputchartselectedandinputchart_selected and inputchart_hovered, which contain the data_id values of selected and hovered elements. Server code can react to these inputs, filtering data or updating other outputs based on chart interactions. For R developers who need interactive ggplot charts without the learning curve of a new plotting library, ggiraph offers the fastest path from static to interactive — the ggplot2 code you already know gains hover, click, and selection behaviour with minimal changes.

Common pitfalls

No tooltip appearing. You forgot to wrap the plot with girafe(). Without it, the graphic renders as a static ggplot2 image, no interactivity is added.

Hover CSS not having any effect. The data_id aesthetic is required for CSS-based hover and selection effects. Without it, the browser cannot identify individual SVG elements.

Tooltip showing NA. The tooltip aesthetic must be character. Wrap numeric or factor values with as.character() or paste().

Plot renders at the wrong size. Adjust width_svg and height_svg in girafe(), then control scaling with opts_sizing().

See also


ggiraph is maintained by David Gohel. The source is on GitHub and the full documentation is at davidgohel.github.io/ggiraph.