You’re a Shiny developer. You’ve built dashboards that work, but users complain they’re “confusing” or “overwhelming.” You know something’s wrong, but you’re not a UX designer.
Good news: You don’t need to be. This 15-minute guide shows you how to use bidux to get concrete, actionable suggestions for improving your dashboard using behavioral science principles.
You’ll learn:
Who this is for: Shiny developers who understand
fluidRow() and selectInput() but aren’t UX
experts.
Let’s start with the three most common dashboard problems and how bidux helps you fix them.
You’ve added filter after filter because stakeholders asked for them. Now your sidebar is a mile long and users are paralyzed.
Quick diagnosis with bidux:
# Document what you're observing
filter_problem <- bid_notice(
previous_stage = bid_interpret(
central_question = "Why aren't users finding insights in my dashboard?"
),
problem = "Users are overwhelmed by too many filter options",
evidence = "12 filter controls in sidebar, user testing shows 70% give up within 2 minutes"
)
# bidux automatically suggests the right theory
print(filter_problem$theory) # "Cognitive Load Theory"Get structure suggestions:
# Get layout recommendations
structure_result <- bid_structure(previous_stage = filter_problem)
# bidux recommends "breathable" layout to reduce cognitive load
print(structure_result$layout)
print(structure_result$layout_rationale)
# See concrete suggestions organized by UX concept
View(structure_result$suggestions)Apply the fix:
# BEFORE: All filters visible at once
ui_before <- sidebarPanel(
dateRangeInput("dates", "Date Range"),
selectInput("region", "Region", choices = regions),
selectInput("product", "Product", choices = products),
selectInput("segment", "Customer Segment", choices = segments),
selectInput("channel", "Marketing Channel", choices = channels),
numericInput("min_revenue", "Min Revenue", 0),
numericInput("max_revenue", "Max Revenue", 1000000),
checkboxGroupInput("status", "Status", choices = statuses),
radioButtons("currency", "Currency", choices = c("USD", "EUR")),
sliderInput("confidence", "Confidence Level", 0, 100, 95),
selectInput("aggregation", "Aggregation", choices = c("Daily", "Weekly")),
checkboxInput("include_forecast", "Include Forecast")
)
# AFTER: Progressive disclosure with smart defaults
ui_after <- sidebarPanel(
# Core filters only (based on bid_structure suggestions)
h4("Quick Filters"),
dateRangeInput("dates", "Date Range", start = Sys.Date() - 30, end = Sys.Date()),
selectInput("region", "Region", choices = c("All", regions), selected = "All"),
# Progressive disclosure for advanced options
actionButton("show_advanced", "More Filters...", class = "btn btn-sm btn-outline-secondary"),
conditionalPanel(
condition = "input.show_advanced % 2 == 1",
h5("Advanced Filters", style = "margin-top: 15px;"),
selectInput("product", "Product", choices = c("All", products)),
selectInput("segment", "Customer Segment", choices = c("All", segments)),
selectInput("channel", "Marketing Channel", choices = c("All", channels)),
# Even more advanced in accordion
bslib::accordion(
bslib::accordion_panel(
"Revenue Range",
numericInput("min_revenue", "Minimum", 0),
numericInput("max_revenue", "Maximum", 1000000)
),
bslib::accordion_panel(
"Display Options",
radioButtons("currency", "Currency", choices = c("USD", "EUR")),
selectInput("aggregation", "Aggregation", choices = c("Daily", "Weekly")),
checkboxInput("include_forecast", "Include Forecast")
)
)
)
)Result: Cognitive load reduced, users see 3 filters instead of 12, advanced options available when needed.
Users open your dashboard and stare at it. They don’t know where to look first or what’s important.
Quick diagnosis:
# Document the navigation confusion
nav_problem <- bid_notice(
previous_stage = bid_interpret(
central_question = "What's the most important insight users need?"
),
problem = "Users don't know where to look first - no clear visual hierarchy",
evidence = "Dashboard shows 8 KPIs, 4 charts, 3 tables simultaneously with equal visual weight"
)
# Get suggestions focused on visual hierarchy
structure_nav <- bid_structure(previous_stage = nav_problem)
# Check which UX concepts bidux identified as relevant
structure_nav$conceptsApply the fix:
# BEFORE: Everything has equal visual weight
ui_flat <- fluidRow(
column(3, valueBoxOutput("metric1")),
column(3, valueBoxOutput("metric2")),
column(3, valueBoxOutput("metric3")),
column(3, valueBoxOutput("metric4")),
column(3, valueBoxOutput("metric5")),
column(3, valueBoxOutput("metric6")),
column(3, valueBoxOutput("metric7")),
column(3, valueBoxOutput("metric8"))
)
# AFTER: Clear hierarchy with primary insight first
ui_hierarchy <- layout_columns(
# Primary insight takes visual priority
card(
full_screen = TRUE,
card_header(
"Revenue Performance",
class = "bg-primary text-white"
),
layout_columns(
col_widths = c(6, 6),
value_box(
title = "This Month",
value = "$1.2M",
showcase = bs_icon("graph-up", size = "3em"),
theme = "success",
p("↑ 22% vs target", style = "font-size: 1.1em;")
),
div(
style = "padding: 20px;",
h4("Key Drivers"),
tags$ul(
tags$li("Enterprise sales up 35%"),
tags$li("Customer retention at 94%"),
tags$li(tags$span(
style = "color: #dc3545;",
"⚠ SMB segment declining"
))
)
)
)
),
# Secondary metrics with less visual weight
layout_columns(
col_widths = c(4, 4, 4),
value_box("New Customers", "156", theme = "info"),
value_box("Avg Deal Size", "$7.8K", theme = "info"),
value_box("Win Rate", "34%", theme = "warning")
)
)Result: Users immediately see what matters, secondary details available but not competing for attention.
If you have telemetry data, bidux can automatically identify UX problems from real user behavior.
# Use moderate sensitivity for most dashboards
issues <- bid_telemetry(
"telemetry.sqlite",
thresholds = bid_telemetry_presets("moderate")
)
# Review identified issues
print(issues)
# Output shows:
# - Issue types (unused inputs, delays, errors, navigation, confusion)
# - Severity levels (critical, high, medium, low)
# - Impact rates (% of sessions affected)library(dplyr)
# Filter to critical and high severity
priority_issues <- issues |>
filter(severity %in% c("critical", "high")) |>
arrange(desc(impact_rate))
# See top 3 issues by impact
head(priority_issues, 3)
# Example output:
# issue_id: unused_input_advanced_filter
# severity: high
# impact_rate: 0.92 (92% of sessions never used it)
# problem: "Users are not interacting with the 'advanced_filter' input control"# Start with interpret stage
interpret_stage <- bid_interpret(
central_question = "How can we reduce friction points identified in telemetry?"
)
# Convert top issues to Notice stages
notices <- bid_notices(
priority_issues,
previous_stage = interpret_stage,
max_issues = 3
)
# Address the most critical issue
top_notice <- notices[[1]]
# Get structure suggestions for this specific issue
structure_result <- bid_structure(
previous_stage = top_notice,
telemetry_flags = bid_flags(issues) # Informs layout selection
)
# Review suggestions
print(structure_result$layout)
print(structure_result$layout_rationale)# Extract global insights
flags <- bid_flags(issues)
# Use flags to inform your approach
if (flags$has_confusion_patterns) {
message("Consider simplifying unclear inputs")
}
if (flags$has_navigation_issues) {
message("Review tab/page organization")
}
if (flags$has_delay_issues) {
message("Users taking too long to engage - review initial view")
}
# See full flag details
str(flags)The quick approaches above work great for isolated problems. Use the full 5-stage BID framework when:
Document your design decisions systematically from the start:
# Stage 1: Interpret - Understand your users
interpret <- bid_interpret(
central_question = "How can marketing managers optimize campaign spend?",
data_story = new_data_story(
hook = "Campaign ROI varies significantly across channels",
context = "Marketing team managing $2M annual budget across 6 channels",
tension = "Current reporting is fragmented, makes optimization difficult",
resolution = "Unified view with clear recommendations"
),
user_personas = data.frame(
name = "Marketing Manager",
goals = "Identify underperforming campaigns, reallocate budget effectively",
pain_points = "Too much data, not enough actionable insights",
technical_level = "intermediate"
)
)
# Stage 2: Notice - Identify specific problems
notice <- bid_notice(
previous_stage = interpret,
problem = "Users overwhelmed by comparing 6 channels across 10 metrics",
evidence = "User interviews show decision paralysis with current 60-cell comparison grid"
)
# Stage 3: Anticipate - Address cognitive biases
anticipate <- bid_anticipate(
previous_stage = notice,
bias_mitigations = list(
anchoring = "Show performance relative to goals, not just absolute numbers",
recency_bias = "Include trend analysis, not just current snapshot",
confirmation_bias = "Highlight both successes and failures"
)
)
# Stage 4: Structure - Get layout and component suggestions
structure <- bid_structure(previous_stage = anticipate)
# Stage 5: Validate - Ensure actionable outcomes
validate <- bid_validate(
previous_stage = structure,
summary_panel = "Executive summary with top 3 actions",
collaboration = "Team can comment on specific campaigns",
next_steps = c(
"Review underperforming channels",
"Implement suggested budget reallocation",
"Schedule follow-up in 2 weeks"
)
)
# Generate implementation report
report <- bid_report(validate, format = "markdown")Combine telemetry analysis with systematic redesign:
# Analyze telemetry
issues <- bid_telemetry("telemetry.sqlite")
# Start BID workflow with telemetry insights
interpret <- bid_interpret(
central_question = "How can we address the top 3 UX friction points?",
data_story = new_data_story(
hook = paste("Analysis of", bid_flags(issues)$session_count, "user sessions"),
tension = "Users struggling with specific UI elements",
resolution = "Systematic improvements using BID framework"
)
)
# Convert telemetry issues to notices
notices <- bid_notices(issues, previous_stage = interpret, max_issues = 3)
# Continue through framework for each issue
# (see full example in getting-started vignette)The BID framework creates a documented trail of reasoning:
# Complete all 5 stages...
validate <- bid_validate(previous_stage = structure, ...)
# Generate report for stakeholders
html_report <- bid_report(validate, format = "html")
# Shows:
# - User needs and personas
# - Identified problems with evidence
# - Bias mitigation strategies
# - Layout selection rationale
# - Specific component recommendations
# - Validation approachPick one problematic section of your dashboard:
# Focus on just your filter panel
filter_notice <- bid_notice(
previous_stage = bid_interpret(
central_question = "How can users filter data more easily?"
),
problem = "Filter panel is cluttered and overwhelming",
evidence = "8 filters visible, users report confusion"
)
# Get specific suggestions for this one area
filter_structure <- bid_structure(previous_stage = filter_notice)Telemetry sensitivity matters:
# During development - catch everything
strict <- bid_telemetry_presets("strict")
dev_issues <- bid_telemetry("telemetry.sqlite", thresholds = strict)
# Production monitoring - major issues only
relaxed <- bid_telemetry_presets("relaxed")
prod_issues <- bid_telemetry("telemetry.sqlite", thresholds = relaxed)
# Most dashboards - balanced approach
moderate <- bid_telemetry_presets("moderate")
issues <- bid_telemetry("telemetry.sqlite", thresholds = moderate)Don’t try to fix everything at once:
Use multiple BID concepts in one component:
# Get suggestions from bid_structure
structure <- bid_structure(previous_stage = notice)
# Check which concepts were identified
print(structure$concepts)
# Apply multiple concepts to one section:
ui_combined <- card(
card_header("Key Metrics"), # Visual Hierarchy - clear header
# Cognitive Load Theory - limit initial choices
value_box("Primary Metric", "87%", theme = "success"),
# Progressive Disclosure - details on demand
actionButton("show_breakdown", "View Breakdown"),
conditionalPanel(
condition = "input.show_breakdown",
# Principle of Proximity - related items grouped
layout_columns(
col_widths = c(4, 4, 4),
value_box("Metric A", "45%"),
value_box("Metric B", "23%"),
value_box("Metric C", "19%")
)
)
)# After running bid_structure...
structure <- bid_structure(previous_stage = notice)
# Get bslib-specific suggestions
bslib_suggestions <- bid_suggest_components(structure, package = "bslib")
# Get reactable suggestions for data display
table_suggestions <- bid_suggest_components(structure, package = "reactable")
# See all available package suggestions
all_suggestions <- bid_suggest_components(structure, package = "all")bid_notice()bid_structure()Remember:
The difference between a confusing dashboard and an effective one isn’t adding more features - it’s applying behavioral science to help users think.
Now go make your dashboard better. Your users will thank you.