Introduction
The linkeR
package provides seamless integration with
plotly
charts, enabling interactive linking between plotly
visualizations and other Shiny components. This vignette demonstrates
how to create linked plotly charts with automatic selection
highlighting.
Key Features
- Automatic linking with any plotly chart type (scatter, bar, histogram, etc.)
- Native selection highlighting using plotly’s built-in selectedpoints mechanism
- Zero-configuration setup for most common use cases
- Custom behaviors for advanced interactions
- Multi-trace support for complex visualizations
Basic Plotly Linking
Simple Example
The most basic way to link a plotly chart is using the
link_plots()
function:
library(shiny)
library(plotly)
library(linkeR)
# Sample data
sample_data <- data.frame(
id = 1:20,
name = paste("Item", 1:20),
x_value = runif(20, 1, 10),
y_value = runif(20, 1, 10),
category = sample(c("A", "B", "C"), 20, replace = TRUE),
value = runif(20, 100, 1000)
)
ui <- fluidPage(
titlePanel("Basic Plotly Linking"),
fluidRow(
column(6,
h4("Interactive Scatter Plot"),
plotlyOutput("scatter_plot")
),
column(6,
h4("Data Table"),
DTOutput("data_table")
)
),
verbatimTextOutput("selection_info")
)
server <- function(input, output, session) {
# Create reactive data
data_reactive <- reactive({ sample_data })
# Simple one-line linking setup
registry <- link_plots(
session,
scatter_plot = data_reactive,
data_table = data_reactive,
shared_id_column = "id"
)
# Create plotly chart with key parameter for reliable linking
output$scatter_plot <- renderPlotly({
plot_ly(
data = sample_data,
x = ~x_value,
y = ~y_value,
color = ~category,
key = ~id, # Essential for reliable linking
source = "scatter_plot",
type = "scatter",
mode = "markers"
) %>%
layout(title = "Click any point to see linking")
})
# Create data table
output$data_table <- renderDT({
datatable(
sample_data,
selection = "single",
options = list(pageLength = 5)
)
})
# Show selection information
output$selection_info <- renderText({
selection <- registry$get_selection()
if (!is.null(selection$selected_id)) {
paste("Selected ID:", selection$selected_id,
"| Source:", selection$source)
} else {
"No selection"
}
})
}
# Run the app
if (interactive()) {
shinyApp(ui = ui, server = server)
}
Important Note: Enabling Visual Highlighting
For DEFAULT plotly selection highlighting to work with linkeR, you must add a custom JavaScript message handler to your Shiny UI. This enables linkeR to send selection updates to the plotly chart for native visual feedback.
Add the following to your fluidPage()
or
dashboardPage()
UI definition:
tags$script(HTML("
Shiny.addCustomMessageHandler('eval', function(code) {
try {
eval(code);
} catch(e) {
console.error('JavaScript execution error:', e);
}
});
"))
This is required for linkeR’s native selection highlighting to work in all linked plotly charts.
Essential Plotly Parameters
For reliable linking, always include these parameters in your
plot_ly()
calls:
The key
Parameter
The key
parameter is crucial for linking. It should
reference your shared ID column:
plot_ly(
data = my_data,
x = ~x_column,
y = ~y_column,
key = ~id_column, # This enables reliable point identification
source = "my_plot"
)
The source
Parameter
The source
parameter identifies your plot for event
handling:
plot_ly(
data = my_data,
x = ~x_column,
y = ~y_column,
key = ~id_column,
source = "unique_plot_name" # Must match your output ID
)
Chart Types Support
linkeR
works with all plotly chart types through native
selectedpoints highlighting:
Scatter Plots
output$scatter <- renderPlotly({
plot_ly(
data = data,
x = ~x_value,
y = ~y_value,
key = ~id,
source = "scatter",
type = "scatter",
mode = "markers"
)
})
Bar Charts
output$bar_chart <- renderPlotly({
plot_ly(
data = aggregated_data,
x = ~category,
y = ~total_value,
key = ~category_id, # Use appropriate ID for linking
source = "bar_chart",
type = "bar"
)
})
Line Charts
output$line_chart <- renderPlotly({
plot_ly(
data = time_series_data,
x = ~date,
y = ~value,
key = ~observation_id,
source = "line_chart",
type = "scatter",
mode = "lines+markers"
)
})
Multi-trace Charts
For charts with multiple traces (e.g., using
color = ~category
), linkeR handles the complexity
automatically:
output$multi_trace <- renderPlotly({
plot_ly(
data = data,
x = ~x_value,
y = ~y_value,
color = ~category, # Creates multiple traces
key = ~id, # Still works perfectly
source = "multi_trace",
type = "scatter",
mode = "markers"
)
})
Advanced Integration
Multiple Plotly Charts
Link multiple plotly charts together for coordinated views:
ui <- fluidPage(
titlePanel("Multiple Linked Plotly Charts"),
fluidRow(
column(4,
h4("Scatter Plot"),
plotlyOutput("scatter", height = "300px")
),
column(4,
h4("Bar Chart"),
plotlyOutput("bar", height = "300px")
),
column(4,
h4("Box Plot"),
plotlyOutput("box", height = "300px")
)
),
verbatimTextOutput("multi_selection")
)
server <- function(input, output, session) {
data_reactive <- reactive({ sample_data })
# Link all three charts
registry <- link_plots(
session,
scatter = data_reactive,
bar = data_reactive,
box = data_reactive,
shared_id_column = "id"
)
# Scatter plot
output$scatter <- renderPlotly({
plot_ly(
data = sample_data,
x = ~x_value,
y = ~y_value,
key = ~id,
source = "scatter"
)
})
# Aggregated bar chart
bar_data <- sample_data %>%
group_by(category) %>%
summarise(
mean_value = mean(value),
id = first(id), # Use first ID for linking
.groups = 'drop'
)
output$bar <- renderPlotly({
plot_ly(
data = bar_data,
x = ~category,
y = ~mean_value,
key = ~id,
source = "bar",
type = "bar"
)
})
# Box plot
output$box <- renderPlotly({
plot_ly(
data = sample_data,
y = ~value,
color = ~category,
key = ~id,
source = "box",
type = "box"
)
})
output$multi_selection <- renderText({
selection <- registry$get_selection()
paste("Selected:", selection$selected_id %||% "None")
})
}
Mixed Component Types
Combine plotly charts with other interactive components:
ui <- fluidPage(
titlePanel("Mixed Component Dashboard"),
fluidRow(
column(3,
h4("Map View"),
leafletOutput("map", height = "400px")
),
column(4,
h4("Performance Chart"),
plotlyOutput("performance", height = "400px")
),
column(5,
h4("Data Details"),
DTOutput("details")
)
)
)
server <- function(input, output, session) {
business_data <- reactive({
data.frame(
business_id = 1:50,
name = paste("Business", 1:50),
latitude = runif(50, 40.7, 40.8),
longitude = runif(50, -111.95, -111.85),
revenue = runif(50, 100000, 1000000),
employees = sample(10:500, 50),
category = sample(c("Tech", "Retail", "Food"), 50, replace = TRUE)
)
})
# Link map, chart, and table
registry <- link_plots(
session,
map = business_data,
performance = business_data,
details = business_data,
shared_id_column = "business_id"
)
# Map
output$map <- renderLeaflet({
data <- business_data()
leaflet(data) %>%
addTiles() %>%
addMarkers(
lng = ~longitude,
lat = ~latitude,
layerId = ~business_id,
popup = ~name
)
})
# Performance chart
output$performance <- renderPlotly({
data <- business_data()
plot_ly(
data = data,
x = ~employees,
y = ~revenue,
color = ~category,
key = ~business_id,
source = "performance",
text = ~paste("Name:", name),
hovertemplate = "%{text}<br>Employees: %{x}<br>Revenue: $%{y:,.0f}<extra></extra>"
) %>%
layout(
title = "Revenue vs Employees",
xaxis = list(title = "Employees"),
yaxis = list(title = "Revenue ($)")
)
})
# Data table
output$details <- renderDT({
datatable(
business_data(),
selection = "single",
options = list(pageLength = 8, scrollX = TRUE)
) %>%
formatCurrency("revenue", currency = "$", digits = 0)
})
}
Selection Highlighting
linkeR
uses plotly’s native selectedpoints
mechanism for highlighting, which provides:
- Consistent behavior across all chart types
- Automatic dimming of unselected points
- Enhanced styling for selected points
- Multi-trace support without additional configuration
Troubleshooting
Common Issues
Problem: Linking doesn’t work or selection highlighting is missing
Solutions: 1. Always include
key = ~id_column
in your plot_ly()
call 2.
Ensure the source
parameter matches your output ID 3.
Verify your shared ID column exists in the data 4. Check that
register_plotly()
or link_plots()
is called
correctly
Problem: Multiple traces don’t highlight correctly
Solution: linkeR handles multi-trace plots automatically. Ensure you’re using the same ID column across all traces.
Problem: There is no plotly visual update on selection
Solution: Make sure the custom JavaScript handler is included in your UI for selection highlighting to work.
Best Practices
-
Always use the
key
parameter for reliable point identification -
Set consistent
source
names that match your output IDs - Use meaningful ID columns that uniquely identify your data points
- Test with different chart types to ensure consistent behavior
- Keep data reactive to ensure updates propagate correctly
Complete Example
Here’s a complete, runnable example demonstrating plotly integration:
library(shiny)
library(plotly)
library(linkeR)
library(DT)
library(dplyr)
# Generate sample data
set.seed(123)
categories <- c("Electronics", "Clothing", "Books")
n <- 30
sample_data <- data.frame(
business_id = paste0("PROD_", sprintf("%03d", 1:n)),
name = paste("Product", LETTERS[1:n]),
price = round(runif(n, 10, 100), 2),
sales = round(runif(n, 100, 1000), 0),
category = sample(categories, n, replace = TRUE),
rating = round(runif(n, 1, 5), 1),
stringsAsFactors = FALSE
)
# Defensive: Remove any rows with NA in key columns
sample_data <- subset(sample_data, !is.na(business_id) & !is.na(name) & !is.na(category))
ui <- fluidPage(
titlePanel("Complete Plotly + linkeR Example"),
tags$script(HTML("
Shiny.addCustomMessageHandler('eval', function(code) {
try {
eval(code);
} catch(e) {
console.error('JavaScript execution error:', e);
}
});
")),
fluidRow(
column(7,
h4("Scatter Plot"),
plotlyOutput("scatter_plot", height = "400px"),
br(),
verbatimTextOutput("current_selection")
),
column(5,
h4("Data Table"),
DTOutput("data_table")
)
)
)
server <- function(input, output, session) {
data_reactive <- reactive({ sample_data })
# Use a fresh registry name to avoid conflicts
scatter_registry <- link_plots(
session,
scatter_plot = data_reactive,
data_table = data_reactive,
shared_id_column = "business_id"
)
# Scatter plot
output$scatter_plot <- renderPlotly({
plot_ly(
data = sample_data,
x = ~price,
y = ~sales,
color = ~category,
key = ~business_id,
source = "scatter_plot",
text = ~paste("Product:", name, "<br>Category:", category, "<br>Rating:", rating),
hovertemplate = "%{text}<br>Price: $%{x:.2f}<br>Sales: %{y:.0f}<extra></extra>",
type = "scatter",
mode = "markers"
) %>%
layout(
title = "Price vs Sales by Category",
xaxis = list(title = "Price ($)"),
yaxis = list(title = "Sales")
)
})
# Data table
output$data_table <- renderDT({
datatable(
sample_data,
selection = "single",
rownames = FALSE,
options = list(
pageLength = 10,
scrollX = TRUE,
searchHighlight = TRUE
)
) %>%
formatCurrency("price", currency = "$") %>%
formatRound(c("sales", "rating"), digits = c(0, 1))
})
# Show current selection
output$current_selection <- renderText({
selection <- scatter_registry$get_selection()
if (!is.null(selection$selected_id)) {
selected_item <- sample_data[sample_data$business_id == selection$selected_id, ]
if (nrow(selected_item) > 0) {
paste0(
"Selected: ", selected_item$name, "\n",
"Category: ", selected_item$category, "\n",
"Price: $", selected_item$price, "\n",
"Sales: ", selected_item$sales, "\n",
"Rating: ", selected_item$rating, "\n",
"Source: ", selection$source
)
} else {
"No item selected"
}
} else {
"No item selected"
}
})
}
# Run the application
if (interactive()) {
shinyApp(ui = ui, server = server)
}
Summary
The linkeR
package makes it easy to create interactive
plotly charts that work seamlessly with other Shiny components. Key
takeaways:
- Use
key = ~id_column
for reliable linking - Set
source
parameter to match output IDs
- linkeR handles all chart types automatically
- Native plotly highlighting provides consistent visual feedback
- Mix plotly charts with maps, tables, and other components effortlessly
For more examples, see the other vignettes and the package’s example applications.