Design effective KPI dashboards with metrics selection, visualization best practices, and real-time monitoring patterns...
Comprehensive patterns for designing effective Key Performance Indicator (KPI) dashboards that drive business decisions.
resources/implementation-playbook.md.| Level | Focus | Update Frequency | Audience |
|---|---|---|---|
| Strategic | Long-term goals | Monthly/Quarterly | Executives |
| Tactical | Department goals | Weekly/Monthly | Managers |
| Operational | Day-to-day | Real-time/Daily | Teams |
Specific: Clear definition
Measurable: Quantifiable
Achievable: Realistic targets
Relevant: Aligned to goals
Time-bound: Defined period
āāā Executive Summary (1 page)
ā āāā 4-6 headline KPIs
ā āāā Trend indicators
ā āāā Key alerts
āāā Department Views
ā āāā Sales Dashboard
ā āāā Marketing Dashboard
ā āāā Operations Dashboard
ā āāā Finance Dashboard
āāā Detailed Drilldowns
āāā Individual metrics
āāā Root cause analysis
Revenue Metrics:
- Monthly Recurring Revenue (MRR)
- Annual Recurring Revenue (ARR)
- Average Revenue Per User (ARPU)
- Revenue Growth Rate
Pipeline Metrics:
- Sales Pipeline Value
- Win Rate
- Average Deal Size
- Sales Cycle Length
Activity Metrics:
- Calls/Emails per Rep
- Demos Scheduled
- Proposals Sent
- Close Rate
Acquisition:
- Cost Per Acquisition (CPA)
- Customer Acquisition Cost (CAC)
- Lead Volume
- Marketing Qualified Leads (MQL)
Engagement:
- Website Traffic
- Conversion Rate
- Email Open/Click Rate
- Social Engagement
ROI:
- Marketing ROI
- Campaign Performance
- Channel Attribution
- CAC Payback Period
Usage:
- Daily/Monthly Active Users (DAU/MAU)
- Session Duration
- Feature Adoption Rate
- Stickiness (DAU/MAU)
Quality:
- Net Promoter Score (NPS)
- Customer Satisfaction (CSAT)
- Bug/Issue Count
- Time to Resolution
Growth:
- User Growth Rate
- Activation Rate
- Retention Rate
- Churn Rate
Profitability:
- Gross Margin
- Net Profit Margin
- EBITDA
- Operating Margin
Liquidity:
- Current Ratio
- Quick Ratio
- Cash Flow
- Working Capital
Efficiency:
- Revenue per Employee
- Operating Expense Ratio
- Days Sales Outstanding
- Inventory Turnover
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā EXECUTIVE DASHBOARD [Date Range ā¼] ā
āāāāāāāāāāāāāāā¬āāāāāāāāāāāāāā¬āāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāā¤
ā REVENUE ā PROFIT ā CUSTOMERS ā NPS SCORE ā
ā $2.4M ā $450K ā 12,450 ā 72 ā
ā ā² 12% ā ā² 8% ā ā² 15% ā ā² 5pts ā
āāāāāāāāāāāāāāā“āāāāāāāāāāāāāā“āāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāā¤
ā ā
ā Revenue Trend ā Revenue by Product ā
ā āāāāāāāāāāāāāāāāāāāāāāāāā ā āāāāāāāāāāāāāāāāāāāā ā
ā ā /\ /\ ā ā ā āāāāāāāā 45% ā ā
ā ā / \ / \ /\ ā ā ā āāāāāā 32% ā ā
ā ā / \/ \ / \ ā ā ā āāāā 18% ā ā
ā ā / \/ \ ā ā ā āā 5% ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāā ā āāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā š“ Alert: Churn rate exceeded threshold (>5%) ā
ā š” Warning: Support ticket volume 20% above average ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā SAAS METRICS Jan 2024 [Monthly ā¼] ā
āāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā āāāāāāāāāāāāāāāāāā ā MRR GROWTH ā
ā ā MRR ā ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā $125,000 ā ā ā /āā ā ā
ā ā ā² 8% ā ā ā /āāāā/ ā ā
ā āāāāāāāāāāāāāāāāāā ā ā /āāāā/ ā ā
ā āāāāāāāāāāāāāāāāāā ā ā /āāāā/ ā ā
ā ā ARR ā ā ā /āāāā/ ā ā
ā ā $1,500,000 ā ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā ā² 15% ā ā J F M A M J J A S O N D ā
ā āāāāāāāāāāāāāāāāāā ā ā
āāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā UNIT ECONOMICS ā COHORT RETENTION ā
ā ā ā
ā CAC: $450 ā Month 1: āāāāāāāāāāāāāāāāāāāā 100% ā
ā LTV: $2,700 ā Month 3: āāāāāāāāāāāāāāāāā 85% ā
ā LTV/CAC: 6.0x ā Month 6: āāāāāāāāāāāāāāāā 80% ā
ā ā Month 12: āāāāāāāāāāāāāā 72% ā
ā Payback: 4 months ā ā
āāāāāāāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā CHURN ANALYSIS ā
ā āāāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāā ā
ā ā Gross ā Net ā Logo ā Expansion ā ā
ā ā 4.2% ā 1.8% ā 3.1% ā 2.4% ā ā
ā āāāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā OPERATIONS CENTER Live ā Last: 10:42:15 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā SYSTEM HEALTH ā SERVICE STATUS ā
ā āāāāāāāāāāāāāāāāāāāāāāāā ā ā
ā ā CPU MEM DISK ā ā ā API Gateway Healthy ā
ā ā 45% 72% 58% ā ā ā User Service Healthy ā
ā ā āāā āāāā āāā ā ā ā Payment Service Degraded ā
ā ā āāā āāāā āāā ā ā ā Database Healthy ā
ā ā āāā āāāā āāā ā ā ā Cache Healthy ā
ā āāāāāāāāāāāāāāāāāāāāāāāā ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā REQUEST THROUGHPUT ā ERROR RATE ā
ā āāāāāāāāāāāāāāāāāāāāāāāā ā āāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā āāāāā
āāāāāā
āāāāāāāā
ā ā ā āāāāāāāāāāāāāāāāāāāā ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāā ā āāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā Current: 12,450 req/s ā Current: 0.02% ā
ā Peak: 18,200 req/s ā Threshold: 1.0% ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā RECENT ALERTS ā
ā 10:40 š” High latency on payment-service (p99 > 500ms) ā
ā 10:35 š¢ Resolved: Database connection pool recovered ā
ā 10:22 š“ Payment service circuit breaker tripped ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Define reporting timezone, currency/conversion date, numerator/denominator and comparison period for each card. Do not mix percent changes with percentage points or NPS points. ARR = 12 Ć MRR implies equal growth rates when using the same period and definition. These synthetic layout figures are not reconciled accounts. Require access controls, aggregation/minimum cohort sizes and freshness/coverage labels before showing customer/employee or financial data; avoid exporting raw PII. Email opens/clicks can include privacy proxies/bots and are not direct engagement proof.
Use resources/metric-queries.sql for executable normalized-input examples. users contains one row per customer, events contains user/month observations, spend contains source spend rows, and monthly_revenue contains one reconciled amount per month. Represent months as an integer year Ć 12 + month ā 1 so offsets do not wrap after December.
The queries keep the original cohort denominator, deduplicate activity before counting, aggregate spend before joining customers, and return NULL for undefined growth or acquisition cost. Missing cohort/month combinations need an explicit reporting calendar if zero-activity rows must be displayed. The growth query compares consecutive available rows; require a complete monthly calendar or label gaps as unavailable rather than calling a multi-month change monthly growth. Match spend and acquired-customer channel/time cohorts before interpreting CAC. Normalize subscription intervals, currency, refunds and revenue recognition before producing monthly revenue; these examples do not define accounting policy.
import streamlit as st
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
st.set_page_config(page_title="KPI Dashboard", layout="wide")
# Synthetic layout illustration; this is not a connected or filterable dashboard.
# Header with illustrative date selector
col1, col2 = st.columns([3, 1])
with col1:
st.title("Executive Dashboard")
with col2:
date_range = st.selectbox(
"Period",
["Last 7 Days", "Last 30 Days", "Last Quarter", "YTD"]
)
# KPI Cards
def metric_card(label, value, delta, prefix="", suffix="", delta_unit="%", lower_is_better=False):
st.metric(
label=label,
value=f"{prefix}{value:,.1f}{suffix}",
delta=f"{delta:+.1f}{delta_unit}",
delta_color="inverse" if lower_is_better else "normal",
)
col1, col2, col3, col4 = st.columns(4)
with col1:
metric_card("Revenue", 2400000, 12.5, prefix="$")
with col2:
metric_card("Customers", 12450, 15.2)
with col3:
metric_card("NPS Score", 72, 5.0, delta_unit=" pts")
with col4:
metric_card("Churn Rate", 4.2, -0.8, suffix="%", delta_unit=" pp", lower_is_better=True)
# Charts
col1, col2 = st.columns(2)
with col1:
st.subheader("Revenue Trend")
revenue_data = pd.DataFrame({
'Month': pd.date_range('2024-01-01', periods=12, freq='ME'),
'Revenue': [180000, 195000, 210000, 225000, 240000, 255000,
270000, 285000, 300000, 315000, 330000, 345000]
})
fig = px.line(revenue_data, x='Month', y='Revenue',
line_shape='spline', markers=True)
fig.update_layout(height=300)
st.plotly_chart(fig, use_container_width=True)
with col2:
st.subheader("Revenue by Product")
product_data = pd.DataFrame({
'Product': ['Enterprise', 'Professional', 'Starter', 'Other'],
'Revenue': [45, 32, 18, 5]
})
fig = px.pie(product_data, values='Revenue', names='Product',
hole=0.4)
fig.update_layout(height=300)
st.plotly_chart(fig, use_container_width=True)
# Cohort Heatmap
st.subheader("Cohort Retention")
cohort_data = pd.DataFrame({
'Cohort': ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
'M0': [100, 100, 100, 100, 100],
'M1': [85, 87, 84, 86, 88],
'M2': [78, 80, 76, 79, None],
'M3': [72, 74, 70, None, None],
'M4': [68, 70, None, None, None],
})
fig = go.Figure(data=go.Heatmap(
z=cohort_data.iloc[:, 1:].values,
x=['M0', 'M1', 'M2', 'M3', 'M4'],
y=cohort_data['Cohort'],
colorscale='Blues',
text=cohort_data.iloc[:, 1:].values,
texttemplate='%{text}%',
textfont={"size": 12},
))
fig.update_layout(height=250)
st.plotly_chart(fig, use_container_width=True)
# Alerts Section
st.subheader("Alerts")
alerts = [
{"level": "error", "message": "Illustrative alert: no live threshold evaluation connected"},
{"level": "warning", "message": "Support ticket volume 20% above average"},
]
for alert in alerts:
if alert["level"] == "error":
st.error(f"š“ {alert['message']}")
elif alert["level"] == "warning":
st.warning(f"š” {alert['message']}")