Most developers build a dashboard by listing every metric the database can produce and giving each one a chart. The logic feels airtight: the data exists, so show it. The result is a wall of equal-weight cards, twelve of them, all the same size, all the same color, none of them louder than the rest. You open the page and your eye has nowhere to land. There is information everywhere and an answer nowhere.
That is the everything-dashboard, and it is the most common failure mode in B2B software. It looks busy, which feels like value. It is actually a data dump wearing the costume of a product. The user came to your dashboard with a question in their head — one question, the reason they logged in today — and your screen made them go find the answer themselves among eleven charts they didn't ask for.
A dashboard is not a report. A report is exhaustive on purpose; you read it top to bottom because you have to. A dashboard is glanced at, ten times a day, for two seconds each time. Those two seconds are the entire product. If the user can't get their answer in two seconds, the other fifty-eight charts are decoration.
Here is the everything-dashboard in code, and it is seductive because it is so easy to write.
{metrics.map((m) => (
<Card key={m.id} title={m.label}>
<Chart data={m.series} />
</Card>
))}
One .map(), one grid, ship it. Every metric gets the same Card, the same height, the same chart. The code is clean. The screen is useless. You have offloaded the hardest design decision — what matters most here? — onto the user, and you've done it ten times a day, forever.
The everything-dashboard fails for the same reason a flat hierarchy fails anywhere. When everything is equally loud, nothing is loud. This is One Loudest Thing applied to data: a screen with twelve equal cards has zero loudest things, which is worse than having the wrong one. Run the Squint Test on a typical SaaS dashboard and watch the whole thing dissolve into uniform grey mush. No focal point survives the blur because none was ever designed.
"We already track it" is the worst justification for putting something on the main dashboard. The cost of a metric is not the query — it's the attention it steals from the metric that matters. Every chart you add taxes the one chart the user came for.
The fix is not "fewer charts" as a rule, though you will usually end up with fewer. The fix is deciding, before you write any JSX, what single question this screen exists to answer.
Every dashboard has a first question. It is the thing the user wants to know the instant the page loads, before they touch anything, before they explore. For a sales dashboard it might be "are we going to hit the number this month?" For a server-monitoring tool, "is anything on fire right now?" For an analytics product, "is traffic up or down versus last week?" For a billing dashboard, "how much will I owe?"
Notice these are not metrics. They are questions, phrased the way the user thinks them — anxious, specific, present-tense. Your job is to find that question and make its answer the hero of the screen. Everything else is supporting cast.
A dashboard's job is to answer the user's single most important question in one glance, before any exploration. Design that answer first; everything else on the screen is secondary. If the first question isn't answered above the fold, in under two seconds, you don't have a dashboard — you have a database with a front end.
The discipline of AFQ is that you write the question down — in plain language, as a sentence — before you open the editor. "Will I hit my revenue goal this month?" Now the design has a job. The hero of the screen is the answer to that sentence: a number, a yes/no, a single trend. The eleven other things you were going to show are now auditioning for the supporting role, and most of them won't make the cut.
If the first question isn't answered above the fold in under two seconds, you don't have a dashboard — you have a database with a front end.
How do you find the question if you're not sure? Three ways. Ask what the user does immediately after checking the dashboard — the action reveals the question. Ask what they'd check on their phone in five seconds between meetings. Or look at what they screenshot and paste into Slack: people screenshot the answer, not the dashboard. That screenshot is your hero.
Write the user's first question as a literal sentence before you write any code; the answer to that sentence is the only thing that gets to be the hero.
Once you know the question, the layout falls out of it. A good dashboard has exactly three tiers, and they map to how attention actually decays after the page loads.
The hero. One number — or one chart, or one status — that answers the first question. It is the largest type on the screen and it sits where the eye lands first, which in most layouts is top-left, the start of the Scan Line. This is your One Loudest Thing. There is exactly one. A hero MRR figure might be 48px while the labels around it are 14px; that 3.4x size jump is what tells the eye start here.
Supporting stats. Three to five numbers that contextualize the hero. If the hero is "revenue this month," the supporting row is last month, the goal, the growth rate, the count of new customers. These are medium-weight — bigger than body text, much smaller than the hero. A horizontal row of four KPIs, each ~24px, does this well.
Detail. Everything else: the time-series charts, the breakdowns, the tables. This is where exploration lives, and it belongs below the answer, not competing with it. The user drops here only after the hero has told them whether they need to.
<main>
<Hero metric={revenue} goal={goal} /> {/* one loudest thing */}
<StatRow stats={[lastMonth, growth, newCust]} /> {/* context */}
<section className="charts">{/* detail, on demand */}
<RevenueOverTime />
<RevenueByPlan />
</section>
</main>
The visual weights are the whole game. If your hero is the same size as your supporting stats, you don't have a hierarchy — you have a list. The size jump between tiers should be obvious at a glance, not a subtle 2px difference a designer would notice and a user never will.
A useful constraint: if you can't decide which metric is the hero, your dashboard is trying to serve two users or two questions. Split it. Two focused dashboards beat one that does neither job. The "Overview" tab answers the executive's question; the "Performance" tab answers the operator's. Each gets its own hero.
The single highest-leverage thing on a dashboard is the state it's in before the user changes anything. Most people never change the defaults. Whatever date range, comparison, and grouping you ship as the default is what 80% of users will see 100% of the time. Treat defaults as the product, not as a fallback.
Date range. The default should be the range that answers the first question, not the range that's easy to query. "Last 30 days" is a lazy default. If the question is "am I hitting this month's goal," the default is month-to-date — and you show the comparison to the same point last month, not the full previous month, because comparing a half-finished month to a complete one makes every dashboard look like it's collapsing on the 3rd.
Comparison. A number alone is noise. Forty-two thousand dollars — good or bad? You can't know without a comparison. Every hero metric needs a built-in baseline: versus last period, versus goal, or as a trend. The comparison is what turns a number into an answer.
<Hero
value={revenue}
format="currency"
delta={revenue - lastPeriod} // shows "+12% vs last month"
goal={goal} // shows "84% of $50k goal"
/>
Chart junk. This is where developers leak polish without realizing it, because charting libraries ship with everything turned on. Defaults from Chart.js or Recharts give you gridlines, axis lines, tick marks, a legend, a background, data-point dots, and a border — all at full strength, all competing with the data. Strip it down. The data is the ink that matters; everything else is overhead.
#f1f1f1, or remove them entirely for short series.$48.2k, not $48,231.00. Precision the user can't act on is just clutter.Don't invent your defaults from scratch — this is a job for the Reference Library. Open three dashboards you respect (Stripe, Linear, Vercel) and notice their date range, their comparison, and how little chart chrome they keep. Steal the decisions, not the pixels.
A dashboard with good defaults answers the question on first load with zero clicks. A dashboard with lazy defaults makes the user configure it into usefulness every single time, and most won't bother — they'll glance, get nothing, and leave.
Whatever you ship as the default is what most users will see every time; defaults are the product, not the fallback.
The everything-dashboard has a quieter sibling: the dashboard that only exists in the demo state, with three months of seeded data and every chart full. Then a real new user logs in, the account is empty, and the whole screen is a grid of "0" and flat lines and broken-looking charts. The first impression of your product is a wall of nothing.
A dashboard is the highest-traffic place in your app to apply The Four States Rule. Every panel needs four designs, not one.
Empty. A brand-new account has no data. Don't render a chart of zeros — that reads as broken. Render a short line that says what the metric will show once there's data, and point at the action that creates it: "Your revenue will appear here after your first sale. Connect a payment method." The empty state is onboarding in disguise; it's the most-seen screen of every product and the least-designed.
Loading. Dashboards aggregate, so they're slow — queries take time, and a spinner over the whole page reads as a stall. Use skeleton placeholders shaped like the content: grey blocks where the hero and cards will be. Skeletons make the wait feel shorter because the eye sees structure arriving, not a frozen page. This is Perceived Over Actual — the query takes the same time either way, but the skeleton makes it feel fast.
{isLoading ? (
<div className="skeleton-hero" aria-busy="true" />
) : (
<Hero value={revenue} delta={delta} goal={goal} />
)}
.skeleton-hero {
height: 96px;
border-radius: 12px;
background: linear-gradient(90deg, #f3f4f6 25%, #e5e7eb 37%, #f3f4f6 63%);
background-size: 400% 100%;
animation: shimmer 1.4s ease infinite;
}
@keyframes shimmer { to { background-position: -200% 0; } }
Error. A query fails. Don't blank the panel or, worse, render a misleading "0" that looks like real data. Show a small inline message scoped to the panel — "Couldn't load revenue. Retry." — so one failed query doesn't take down the whole dashboard. Scope errors to the card, not the page.
Ideal. The state you already built: real data, full charts. This is the only one most teams ship, which is exactly why empty and loading states are where you'll out-polish your competition.
The fourth idea here is glance-then-drill — progressive disclosure for dashboards. The top of the screen answers the question; the detail is available but not shouted. A user who's satisfied by the hero never has to scroll. A user who wants to dig clicks into a chart, opens a date picker, drills into a breakdown. You design the glance first and the drill second — never the reverse. The everything-dashboard inverts this: it shows all the drill-down detail up front and makes the user assemble the glance themselves.
Design the two-second glance first and the deep-dive second; a satisfied user should never need to scroll, and a curious one should always be able to.
Progressive disclosure is also how you escape the false choice between "too sparse" and "too dense." You're not hiding the twelve metrics — you're ranking them. The hero is on the surface; the rest are one click down. The dashboard stays calm on first glance and stays powerful on demand.
Before touching code, finish this line for your main dashboard: "When a user opens this, they want to know ___." One sentence, in their words. If you can't write one clear question, your dashboard is serving two users — split it.
Pick the single metric that answers that sentence. Make it the largest type on the screen — aim for a 2.5–3x size jump over the supporting stats — and move it top-left. Run the Squint Test: it should be the one thing that survives the blur.
A lone number is noise. Attach a baseline: a delta versus last period, percent of goal, or a trend arrow. The hero should answer "good or bad?" not just "what?"
Set the default range to whatever answers the first question — usually month-to-date with a same-point-last-period comparison, not "last 30 days." Assume most users never change it.
Turn off your charting library's defaults: remove borders, backgrounds, vertical gridlines, and axis lines; fade horizontal gridlines to near-white; round numbers to two significant figures. Keep the data, cut the chrome.
Replace the full-page spinner with content-shaped skeleton blocks. Then log in with a fresh, empty account and design what you see — point each empty panel at the action that fills it.
A dashboard isn't measured by how much it shows — it's measured by how fast it answers the one question the user came to ask.