While linkeR provides sensible defaults, you often want
custom behaviors when users click on linked components. This vignette
shows how to create rich, interactive experiences.
You can define exactly what happens when a leaflet marker is clicked (either directly or via linking):
server <- function(input, output, session) {
  
  business_data <- reactive({
    # Your business data here
  })
  
  output$business_map <- renderLeaflet({
    # Render your map (no popup needed - custom handler will create it)
    leaflet(business_data()) %>%
      addTiles() %>%
      addCircleMarkers(
        lng = ~longitude,
        lat = ~latitude,
        layerId = ~business_id,
        radius = ~sqrt(revenue/1000) + 3
      )
  })
  
  output$business_table <- renderDT({
    # Your table rendering code
  })
  
  # Link with custom leaflet behavior
  link_plots(
    session,
    business_map = business_data,
    business_table = business_data,
    shared_id_column = "business_id",
    
    # Custom handler for leaflet clicks
    leaflet_click_handler = function(map_proxy, selected_data, session) {
      if (!is.null(selected_data)) {
        # Create rich popup
        popup_content <- paste0(
          "<div style='min-width: 200px;'>",
          "<h4 style='color: #2c3e50;'>", selected_data$name, "</h4>",
          "<p><strong>Revenue:</strong> $", format(selected_data$revenue, big.mark = ","), "</p>",
          "<p><strong>Category:</strong> ", selected_data$category, "</p>",
          "<p><strong>Rating:</strong> ", selected_data$rating, "/5.0 ⭐</p>",
          "</div>"
        )
        
        # Custom zoom and popup
        map_proxy %>%
          leaflet::setView(
            lng = selected_data$longitude, 
            lat = selected_data$latitude, 
            zoom = 15  # Custom zoom level
          ) %>%
          leaflet::clearPopups() %>%
          leaflet::addPopups(
            lng = selected_data$longitude,
            lat = selected_data$latitude,
            popup = popup_content
          )
      } else {
        # Handle deselection
        map_proxy %>% leaflet::clearPopups()
      }
    }
  )
}Similarly, you can customize table selection behavior:
link_plots(
  session,
  business_map = business_data,
  business_table = business_data,
  shared_id_column = "business_id",
  
  # Custom handler for table clicks
  dt_click_handler = function(dt_proxy, selected_data, session) {
    if (!is.null(selected_data)) {
      # Find the row and select it
      current_data <- business_data()
      row_idx <- which(current_data$business_id == selected_data$business_id)
      
      if (length(row_idx) > 0) {
        # Select and scroll to row
        DT::selectRows(dt_proxy, selected = row_idx[1])
        
        # Optional: scroll to the row
        page_size <- 10  # Your page size
        target_page <- ceiling(row_idx[1] / page_size)
        DT::selectPage(dt_proxy, target_page)
        
        # Show additional info
        showNotification(
          paste("Selected:", selected_data$name, "- Revenue: $", 
                format(selected_data$revenue, big.mark = ",")),
          type = "message",
          duration = 3
        )
      }
    } else {
      # Handle deselection
      DT::selectRows(dt_proxy, selected = integer(0))
    }
  }
)You can also add a callback that fires whenever any selection changes:
link_plots(
  session,
  business_map = business_data,
  business_table = business_data,
  shared_id_column = "business_id",
  
  # Global callback for all selection changes
  on_selection_change = function(selected_id, selected_data, source_id, session) {
    if (!is.null(selected_data)) {
      # Update other UI elements
      output$selected_business_info <- renderText({
        paste0(
          "Selected: ", selected_data$name, "\n",
          "Source: ", source_id, "\n",
          "Revenue: $", format(selected_data$revenue, big.mark = ",")
        )
      })
      
      # Log for analytics
      cat("Selection event:", selected_id, "from", source_id, "\n")
      
      # Update other reactive values
      current_selection(selected_data)
    } else {
      # Handle deselection
      output$selected_business_info <- renderText("No selection")
      current_selection(NULL)
    }
  }
)Consistent Experience: Custom handlers should provide the same experience whether triggered by direct clicks or linking
Error Handling: Wrap custom logic in
tryCatch() for robustness
Performance: Keep custom handlers lightweight to avoid UI lag
Visual Feedback: Provide clear visual feedback for user actions
Deselection: Always handle the case where
selected_data is NULL
leaflet_click_handler = function(map_proxy, selected_data, session) {
  if (!is.null(selected_data)) {
    # Different behavior based on data properties
    if (selected_data$risk_level == "High") {
      # Show warning for high-risk items
      showModal(modalDialog(
        title = "High Risk Alert",
        paste("This location has high risk level:", selected_data$name),
        easyClose = TRUE
      ))
    }
    
    # Color popup based on risk
    popup_color <- switch(selected_data$risk_level,
      "Low" = "#d4edda",
      "Medium" = "#fff3cd",
      "High" = "#f8d7da"
    )
    
    popup_content <- paste0(
      "<div style='background-color: ", popup_color, "; padding: 10px; border-radius: 5px;'>",
      "<h4>", selected_data$name, "</h4>",
      "<p>Risk Level: ", selected_data$risk_level, "</p>",
      "</div>"
    )
    
    map_proxy %>%
      leaflet::setView(lng = selected_data$longitude, lat = selected_data$latitude, zoom = 13) %>%
      leaflet::clearPopups() %>%
      leaflet::addPopups(
        lng = selected_data$longitude,
        lat = selected_data$latitude,
        popup = popup_content
      )
  }
}This approach lets you create rich, context-aware interactions that respond intelligently to your data.