The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
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.
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)
}
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.
For reliable linking, always include these parameters in your
plot_ly()
calls:
key
ParameterThe key
parameter is crucial for linking. It should
reference your shared ID column:
linkeR
works with all plotly chart types through native
selectedpoints highlighting:
For charts with multiple traces (e.g., using
color = ~category
), linkeR handles the complexity
automatically:
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")
})
}
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)
})
}
linkeR
uses plotly’s native selectedpoints
mechanism for highlighting, which provides:
When a point is selected in any linked component:
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.
key
parameter for
reliable point identificationsource
names that match
your output IDsHere’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)
}
The linkeR
package makes it easy to create interactive
plotly charts that work seamlessly with other Shiny components. Key
takeaways:
key = ~id_column
for reliable linkingsource
parameter to match output IDsFor more examples, see the other vignettes and the package’s example applications.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.