This vignette walks through common dashboard scenarios that data scientists and Shiny developers encounter, showing how the BID framework transforms user experience while maintaining analytical rigor.
Each example includes:
You’re a data scientist at a SaaS company. Your stakeholders asked for “a dashboard that shows everything about user engagement.” Being thorough, you built exactly that.
# The "show everything" approach
ui_before <- navbarPage(
"User Engagement Analytics",
tabPanel(
"Overview",
fluidRow(
# 12 KPIs across the top
column(2, valueBoxOutput("dau")),
column(2, valueBoxOutput("wau")),
column(2, valueBoxOutput("mau")),
column(2, valueBoxOutput("retention")),
column(2, valueBoxOutput("churn")),
column(2, valueBoxOutput("ltv"))
),
fluidRow(
column(2, valueBoxOutput("sessions")),
column(2, valueBoxOutput("session_duration")),
column(2, valueBoxOutput("pages_per_session")),
column(2, valueBoxOutput("bounce_rate")),
column(2, valueBoxOutput("conversion")),
column(2, valueBoxOutput("revenue"))
),
# Multiple complex charts
fluidRow(
column(6, plotOutput("engagement_trend", height = "400px")),
column(6, plotOutput("cohort_analysis", height = "400px"))
),
fluidRow(
column(4, plotOutput("funnel_chart")),
column(4, plotOutput("retention_curve")),
column(4, plotOutput("ltv_distribution"))
)
),
tabPanel("Segments", "More detailed segmentation..."),
tabPanel("Cohorts", "Cohort analysis details..."),
tabPanel("Funnels", "Conversion funnel details..."),
tabPanel("Revenue", "Revenue analytics..."),
tabPanel("Product", "Product usage analytics...")
)
User feedback: “I can’t find what I’m looking for” and “It’s overwhelming”
What’s happening: - 12+ metrics compete for attention simultaneously - No clear hierarchy of importance - Users don’t know where to look first - Analysis paralysis from too many options
Let’s apply the systematic BID approach:
# Stage 1: Interpret - Understand the real user need
interpret_result <- bid_interpret(
central_question = "How is our user engagement trending, and what needs attention?",
data_story = list(
hook = "User engagement metrics are spread across multiple systems",
context = "Leadership needs quick insights for weekly business reviews",
tension = "Current dashboards take too long to interpret",
resolution = "Provide immediate key insights with drill-down capability"
),
user_personas = list(
list(
name = "Sarah (Product Manager)",
goals = "Quickly spot concerning trends and dive deeper when needed",
pain_points = "Too many metrics to process in limited meeting time",
technical_level = "Intermediate"
),
list(
name = "Mike (Executive)",
goals = "Understand overall health at a glance",
pain_points = "Gets lost in details when just needs the big picture",
technical_level = "Basic"
)
)
)
# Stage 2: Notice - Identify the specific problem
notice_result <- bid_notice(
previous_stage = interpret_result,
problem = "Users experience information overload with 12+ simultaneous metrics",
evidence = "User interviews show 80% struggle to prioritize information, average time-to-insight is 5+ minutes"
)
# Stage 3: Anticipate - Consider cognitive biases
anticipate_result <- bid_anticipate(
previous_stage = notice_result,
bias_mitigations = list(
attention_bias = "Use size and color to direct focus to most important metrics first",
choice_overload = "Implement progressive disclosure - show key metrics, hide advanced analytics until requested",
anchoring = "Lead with the most important business metric to set proper context"
)
)
# Stage 4: Structure - Organize for cognitive efficiency
structure_result <- bid_structure(previous_stage = anticipate_result)
# Stage 5: Validate - Ensure actionable insights
validate_result <- bid_validate(
previous_stage = structure_result,
summary_panel = "Executive summary highlighting key trends and required actions",
collaboration = "Enable commenting and sharing of specific insights",
next_steps = c(
"Focus on the primary engagement metric trend",
"Investigate any red-flag indicators",
"Use drill-down for detailed analysis only when needed"
)
)
# The BID-informed approach: Progressive disclosure with clear hierarchy
ui_after <- page_fillable(
theme = bs_theme(version = 5),
# Primary insight first (addresses anchoring bias)
layout_columns(
col_widths = c(8, 4),
# Key insight panel
card(
card_header(
"📈 Engagement Health Score",
class = "bg-primary text-white"
),
layout_columns(
value_box(
title = "Overall Score",
value = "87/100",
showcase = bs_icon("speedometer2", size = "2em"),
theme = "success",
p(
"↑ 5 points vs. last month",
style = "font-size: 0.9em; color: #666;"
)
),
div(
h5("Key Drivers", style = "margin-bottom: 10px;"),
tags$ul(
tags$li("DAU trending up (+12%)"),
tags$li("Retention stable (73%)"),
tags$li("⚠️ Session duration declining (-8%)")
)
)
)
),
# Action panel
card(
card_header("🎯 Focus Areas"),
div(
tags$div(
class = "alert alert-warning",
tags$strong("Attention needed:"),
br(),
"Session duration declining. Investigate user experience."
),
actionButton(
"investigate_sessions",
"Investigate Session Trends",
class = "btn btn-warning btn-sm"
)
)
)
),
# Secondary metrics (progressive disclosure)
card(
card_header(
div(
style = "display: flex; justify-content: space-between; align-items: center;",
span("📊 Detailed Metrics"),
actionButton(
"toggle_details",
"Show Details",
class = "btn btn-outline-secondary btn-sm"
)
)
),
# Hidden by default, shown on demand
conditionalPanel(
condition = "input.toggle_details % 2 == 1",
layout_columns(
col_widths = c(3, 3, 3, 3),
value_box("DAU", "45.2K", icon = "people"),
value_box("Retention", "73%", icon = "arrow-clockwise"),
value_box("Sessions", "2.1M", icon = "activity"),
value_box("Revenue", "$127K", icon = "currency-dollar")
),
# Charts appear only when details are requested
layout_columns(
col_widths = c(6, 6),
card(
card_header("Engagement Trend"),
plotOutput("engagement_trend_focused", height = "300px")
),
card(
card_header("Key Drivers Analysis"),
plotOutput("drivers_analysis", height = "300px")
)
)
)
)
)
Key improvements:
You’ve built a comprehensive sales dashboard that shows everything the sales team could possibly need. It’s technically perfect but nobody uses it.
ui_sales_before <- fluidPage(
titlePanel("Q4 Sales Performance Dashboard"),
# Massive filter section
sidebarLayout(
sidebarPanel(
dateRangeInput("date_range", "Date Range"),
selectInput("region", "Region", choices = regions, multiple = TRUE),
selectInput(
"product",
"Product Line",
choices = products,
multiple = TRUE
),
selectInput("salesperson", "Sales Rep", choices = reps, multiple = TRUE),
selectInput(
"customer_segment",
"Customer Segment",
choices = segments,
multiple = TRUE
),
selectInput(
"deal_size",
"Deal Size",
choices = deal_sizes,
multiple = TRUE
),
checkboxGroupInput("deal_stage", "Deal Stages", choices = stages),
numericInput("min_value", "Minimum Deal Value", value = 0),
numericInput("max_value", "Maximum Deal Value", value = 1000000),
radioButtons("currency", "Currency", choices = c("USD", "EUR", "GBP")),
actionButton("apply_filters", "Apply Filters", class = "btn-primary")
),
mainPanel(
tabsetPanel(
tabPanel(
"Summary",
fluidRow(
column(3, valueBoxOutput("total_revenue")),
column(3, valueBoxOutput("deal_count")),
column(3, valueBoxOutput("avg_deal_size")),
column(3, valueBoxOutput("win_rate"))
),
plotOutput("revenue_chart", height = "600px")
),
tabPanel("By Region", dataTableOutput("region_table")),
tabPanel("By Product", dataTableOutput("product_table")),
tabPanel("By Rep", dataTableOutput("rep_table")),
tabPanel("Pipeline", dataTableOutput("pipeline_table")),
tabPanel("Forecasting", plotOutput("forecast_chart"))
)
)
)
)
User feedback: “Takes forever to find what I need” and “I just export to Excel instead”
# Apply BID framework focusing on sales manager workflow
sales_interpret <- bid_interpret(
central_question = "What deals need my attention this week?",
data_story = list(
hook = "Sales managers spend 2+ hours weekly creating status reports",
context = "They need to quickly identify at-risk deals and top opportunities",
tension = "Current data requires extensive filtering and analysis",
resolution = "Provide intelligent prioritization with drill-down capability"
),
user_personas = list(
list(
name = "Jennifer (Regional Sales Manager)",
goals = "Identify at-risk deals, spot top opportunities, prepare for team meetings",
pain_points = "Too much filtering required to find actionable insights",
technical_level = "Intermediate"
)
)
)
sales_notice <- bid_notice(
previous_stage = sales_interpret,
problem = "Sales managers overwhelmed by filter complexity and data volume",
evidence = "Users spend average 15 minutes per session just setting up filters, 40% abandon before getting insights"
)
sales_anticipate <- bid_anticipate(
previous_stage = sales_notice,
bias_mitigations = list(
recency_bias = "Show deals by urgency, not just recent activity",
confirmation_bias = "Highlight both positive and concerning trends",
choice_overload = "Limit initial choices to most common use cases"
)
)
sales_structure <- bid_structure(previous_stage = sales_anticipate)
ui_sales_after <- page_navbar(
title = "Sales Command Center",
theme = bs_theme(version = 5, preset = "bootstrap"),
nav_panel(
"🚨 Needs Attention",
layout_columns(
# Immediate action items
card(
card_header(
"🔥 Urgent - Deals at Risk",
class = "bg-danger text-white"
),
card_body(
p("3 deals worth $340K need immediate attention"),
layout_columns(
col_widths = c(6, 6),
div(
h6("MegaCorp Deal - $180K"),
p(
"❌ No activity in 14 days",
style = "color: #dc3545; margin: 0;"
),
p("Owner: Mike Chen", style = "font-size: 0.9em; color: #666;")
),
div(
actionButton(
"view_megacorp",
"View Details",
class = "btn btn-sm btn-outline-danger"
),
actionButton(
"contact_mike",
"Contact Mike",
class = "btn btn-sm btn-danger"
)
)
)
)
),
# Opportunities
card(
card_header("⭐ Hot Opportunities", class = "bg-success text-white"),
card_body(
p("2 deals worth $280K ready to close"),
actionButton(
"view_opportunities",
"Review Opportunities",
class = "btn btn-success btn-sm"
)
)
)
),
# Smart filters (only show when needed)
conditionalPanel(
condition = "input.show_filters",
card(
card_header("🔍 Refine Focus"),
layout_columns(
col_widths = c(3, 3, 3, 3),
selectInput("quick_region", "Region", choices = c("All", regions)),
selectInput(
"quick_timeframe",
"Timeframe",
choices = c("This Week", "This Month", "This Quarter")
),
selectInput(
"quick_value",
"Deal Size",
choices = c("All", ">$50K", ">$100K", ">$250K")
),
actionButton(
"show_all_filters",
"More Filters...",
class = "btn btn-outline-secondary btn-sm"
)
)
)
)
),
nav_panel(
"📊 Performance",
layout_columns(
col_widths = c(4, 4, 4),
value_box(
"This Month",
"$1.2M",
"vs. $980K target (+22%)",
showcase = bs_icon("graph-up"),
theme = "success"
),
value_box(
"Pipeline Health",
"Strong",
"3.2x coverage ratio",
showcase = bs_icon("speedometer2"),
theme = "info"
),
value_box(
"Team Status",
"On Track",
"8 of 10 reps hitting quota",
showcase = bs_icon("people"),
theme = "success"
)
),
card(
card_header("📈 Key Trends"),
plotOutput("performance_trends", height = "400px")
)
),
nav_panel(
"🎯 Team Focus",
# Team-specific insights
p("Individual rep performance and coaching opportunities...")
)
)
Key improvements:
You’re monitoring application performance with detailed technical metrics. Your dashboard is perfect for engineers but executives get lost.
# Interpret stage: Understand different user needs
technical_interpret <- bid_interpret(
central_question = "How is our application performing and what needs attention?",
user_personas = list(
list(
name = "DevOps Engineer",
goals = "Identify performance bottlenecks and system issues",
pain_points = "Needs detailed metrics and historical trends",
technical_level = "Expert"
),
list(
name = "Engineering Manager",
goals = "Understand overall system health and team priorities",
pain_points = "Needs summary view but ability to drill down",
technical_level = "Advanced"
),
list(
name = "Executive",
goals = "Understand business impact of technical issues",
pain_points = "Technical details are overwhelming",
technical_level = "Basic"
)
)
)
# Adaptive interface based on user role
ui_technical_after <- page_sidebar(
sidebar = sidebar(
# Role selector affects entire interface
radioButtons(
"user_role",
"View Mode:",
choices = c(
"Executive Summary" = "executive",
"Management View" = "manager",
"Technical Details" = "engineer"
),
selected = "executive"
)
),
# Executive view: Business impact focus
conditionalPanel(
condition = "input.user_role == 'executive'",
h2("🟢 System Health: Good"),
layout_columns(
col_widths = c(6, 6),
card(
card_header("Business Impact"),
value_box(
"Service Availability",
"99.8%",
"Within SLA targets",
theme = "success"
),
value_box(
"User Experience",
"Good",
"Page loads < 2 seconds",
theme = "success"
)
),
card(
card_header("Action Items"),
div(
class = "alert alert-info",
"✅ No critical issues requiring immediate attention"
),
p("Next scheduled maintenance: Friday 2am")
)
)
),
# Manager view: Balance of summary and detail
conditionalPanel(
condition = "input.user_role == 'manager'",
layout_columns(
col_widths = c(3, 3, 3, 3),
value_box("Uptime", "99.8%", theme = "success"),
value_box("Response Time", "1.2s", theme = "success"),
value_box("Error Rate", "0.02%", theme = "success"),
value_box("Throughput", "15K/min", theme = "info")
),
card(
card_header("System Trends"),
plotOutput("system_trends", height = "300px")
),
card(
card_header("Team Alerts"),
p("2 minor alerts resolved this week"),
actionButton("view_alerts", "View Alert History")
)
),
# Engineer view: Full technical detail
conditionalPanel(
condition = "input.user_role == 'engineer'",
# Comprehensive technical metrics
tabsetPanel(
tabPanel("Performance", "Detailed performance metrics..."),
tabPanel("Infrastructure", "Server and database metrics..."),
tabPanel("Alerts", "Full alert history and configuration..."),
tabPanel("Logs", "System logs and debugging info...")
)
)
)
Key insight: Same data, different presentations based on user cognitive needs and technical expertise.
# ❌ Data-structure driven
ui_wrong <- tabPanel(
"Database Tables",
tabPanel("Users Table", dataTableOutput("users")),
tabPanel("Orders Table", dataTableOutput("orders")),
tabPanel("Products Table", dataTableOutput("products"))
)
# ✅ User-intent driven
ui_right <- tabPanel(
"Customer Insights",
card_body(
h4("What customers need your attention?"),
# Show actionable customer insights
)
)
# ❌ Everything visible at once
ui_dense <- fluidRow(
column(2, metric1),
column(2, metric2),
column(2, metric3),
column(2, metric4),
column(2, metric5),
column(2, metric6)
)
# ✅ Key information first, details on demand
ui_progressive <- div(
value_box("Key Metric", "Primary value"),
actionButton("show_details", "View Supporting Metrics"),
conditionalPanel(
condition = "input.show_details",
# Additional metrics here
)
)
Remember: The goal isn’t to become a UX expert, it’s to apply your analytical thinking to user experience and create more effective data products.
Use bid_concepts()
to explore behavioral science
principles, and check out the behavioral-science-primer
vignette for deeper understanding of the psychological principles behind
these improvements.