How to Configure the KPI Card Visualization
The KPI Card turns a report into a single, prominent number — with an optional trend, a comparison against another period, a target, and a status word that tells you at a glance whether the number is good news.
It is the least demanding visualization in DashboardFox. There is no schema to conform to, no required column names, no category values it expects to find. Point it at almost any report and it renders.
What your data does decide is how much of the card you get. This guide walks the whole sidebar, in the order it appears, and explains why some options are missing until your data can support them. It finishes with the part most people skip until a dashboard takes fifteen seconds to load: keeping a wall of KPI cards fast.
1. What your data lets the card do
Almost every question about KPI data comes down to one thing: does your report have a column that says when each row happened, and can DashboardFox read it?
That question puts you on one of three rungs.
Your period column | What the card can do |
|---|---|
None | Show one number — a single cell, or a total across the report. Compare it against another column or another row. That's the whole card. |
Period labels without a year — | A trend, and one comparison: the period immediately before. No year-over-year. No viewer period switching. No click-through to a date range. |
Real dates, or labels with a year — | Everything. Eight comparison types, roll up from days to months to quarters to years, let viewers change the period on the fly, and click straight through to the exact date range behind the number. |
The gap between rung two and rung three is one character: a year. Section 3 explains why.
Date formats the card understands
If your column holds real dates, you don't need to think about this. It's here for when dates arrive as text.
Format | Example |
|---|---|
ISO date (recommended) |
|
ISO date with time |
|
ISO year and month |
|
Year alone |
|
Year and quarter |
|
Day-month-year, dotted |
|
Day-month-year, short month |
|
Month-day-year, written |
|
Slashed dates |
|
Use ISO (2026-03-15) whenever you have the choice. It is unambiguous everywhere in the world and never gets read as the wrong day.
Text period labels work too, as long as they carry a year: Mar-26, Mar-2026, March 2026, 3/2026, Q3-2026, Quarter 3-2026.
Two-digit years: 00 through 68 are read as the 2000s, 69 through 99 as the 1900s. Mar-26 is March 2026. If your data spans that boundary, write the full year.
2. Title, Display Type, and Value Column
The first three fields sit above everything else and apply to every card type.
KPI Title — the heading above the number. Leave it blank to show the value on its own. Type @ to insert a value from the report.
Select Display Type — choose Big Number / Trend. This guide covers that card.
Select Value Column — the column the card reads its number from. It must be typed as a number, or the card can't add it up. A column of 1200, 1450, 1600 stored as text will display but won't offer Sum or Average. If that happens, override the field's data type — see Formula fields.
3. Value Source
This is the most consequential choice on the card. It decides what the big number means, and it gates half the options below it.
Value Source | The number is | Needs a Period Column? |
|---|---|---|
Single value (cell) | One cell of the report — pick a column and a row | No |
Text, as-is | A cell shown exactly as written — a name, a status, a label | No |
Latest period | The most recent period's figure | Yes |
Total, all periods | Every row added up | Optional |
Latest period is what most people want, and it's the only source that unlocks time-based comparisons, the runtime period switcher, and per-period goals.
When you pick a period-based source, four more fields appear.
Period Column
The column that says when each row happened. Only columns DashboardFox can read as dates or period labels appear here. If your date column isn't in the list, its values aren't consistently readable — see the pitfalls in section 13.
Group by
How rows are gathered into periods before the calculation runs. Daily rows grouped by Month give one figure per month.
Options: Day, Week, Month, Quarter, Year.
The rule only goes one way:
You can always roll up. You can never invent detail you don't have.
Daily rows can be grouped into months, quarters or years. Monthly rows can only go to quarters and years. Group by: Day on monthly data does nothing useful.
Weeks are a display choice, not a data grain. A column of
2026-W12won't be recognized as a period column at all. You can display by week when the underlying data is daily.
Calculation
How the rows inside a period are combined into one number.
Options: Sum, Average, Min, Max, Count.
Week starts on
Only appears when Group by is Week. Monday (ISO-8601) or Sunday. Match whatever your business already uses; changing it later moves every bar in the trend.
4. Number Format and Value Color
Number Format decides how the value is written: None, Fixed point, Percent, Large number (1.2M), or Currency. Decimal places and currency symbol sit here.
Formatting is display only. It never changes the arithmetic.
Value Color is the color of the large number. Text values are colored too.
5. Comparison Options
A comparison adds a small change chip beside the value — ▲ +10.8% — and, in Detailed style, a labelled row of figures beneath it.
Source
What the value is being compared against. The options you see depend on your Value Source.
Source | Available when | Compares against |
|---|---|---|
None | always | nothing |
vs a period (time-based) | Value Source is Latest period, and there's a Period Column | an earlier period |
vs another column | always | the same row, a different column |
vs another row | Single value / Text | the same column, a different row |
vs a delta column | Single value / Text | a column that already holds the change |
Compare to
Only for the time-based source. This is the recipe — which earlier period.
Recipe | What it compares against | Needs |
|---|---|---|
vs previous period | the period immediately before | any Period Column |
Period-to-date vs prior | the same portion of the previous period | data finer than the displayed period |
Same period last year | the same month/quarter last year | a year in the data |
Year-over-year to-date | the same portion of last year | a year, and finer data |
Week over week | the previous week | daily data |
Same day last week | the same weekday last week | daily data |
Same day last year | the same date last year | daily data and a year |
Same week last month | the equivalent week last month | daily data |
You will only be offered recipes your data can actually calculate. If Same period last year isn't there, your period labels have no year. If Period-to-date isn't there, your data isn't finer than what you're displaying.
If a recipe reaches past the end of your data, the card doesn't guess — the comparison simply doesn't render.
Measure from
The point in time the recipe counts back from.
Latest data point — the most recent period in the report. Use this when the report might lag.
Today's date — the real calendar date. Use this when a gap in the data should show as a gap.
Favorable
Higher is better or Lower is better. This colours the change chip green or red. Revenue is higher is better; churn, cost, and response time are lower is better. The card has no way to know which — set it.
Change as
How the change is expressed.
Auto — a percentage, unless the value is already a percentage, in which case percentage points.
Percent (%) · Percentage points (pp) · Multiple (x) · Absolute
Auto is right nearly always. The exception it exists for: a conversion rate that goes from 20% to 25% rose by 5 percentage points, not by 5%.
When prior is zero
Dividing by zero has no good answer, so you choose one.
Option | Shows |
|---|---|
Show no value (—) | nothing — the honest default |
Show ∞% | an infinity symbol |
Show absolute change | the raw difference instead of a percentage |
Use current value | the current number as the change |
Style
Compact — a small change chip beside the value. The prior period goes in the caption.
Detailed — a labelled row beneath the value, with the prior figure and the change in columns.
6. Trend Options
Trend draws a small chart of the value over time: None, Line / area, or Bars.
It needs a Period Column. If Trend isn't offered, your Value Source isn't period-based, or the report has no readable date.
Dense data is grouped automatically. Two years of daily rows in a small tile is noise, so the card coarsens the drawn chart until the shape reads. Your figures are untouched.
Bar Color — only for Bars.
Show points on the line — a dot on each period.
Highlight high & low points — the best period turns green, the worst red.
The trend is never colored by whether the news is good. That's the change chip's job.
7. Status & Thresholds
This is where the card earns its place on a wall. A status label — a coloured word beside the change chip — tells you the number's condition without you having to read it.
Tick Show a status label based on value.
Measured as
What the thresholds are compared against.
Measured as | The thresholds mean | Needs a reference? |
|---|---|---|
The value itself | literal numbers — | No |
Percent of a reference |
| Yes |
Percent change from a reference |
| Yes |
Standard deviations from average | how unusual this period is | No — uses the trend |
Compare to
Only appears for the two percent options. What the value is measured against.
Compare to | Available when |
|---|---|
A fixed number | always — type it into Reference |
The goal | a goal is configured (section 8) |
The prior period | a comparison is set up (section 5) |
The average of all periods | there are at least three periods |
If the reference cannot be worked out, no status word is shown. No goal set, a goal of zero, a missing prior period — the card shows nothing rather than a colour that means nothing. This is deliberate, and it's the behaviour that surprises people most.
Direction, Good at, Bad at
Direction — Higher is better, or Lower is better.
Good at and Bad at are the two cut points. Anything between them is Neutral. The units follow Measured as, and the labels change to remind you: Good at (%), Good at (σ).
Changing Measured as seeds sensible cut points into any field you've left blank — 100 / 90 for percent of, 0 / −10 for percent change, 2 / −2 for standard deviations. It never overwrites a number you've typed.
You can rename and recolour all three tiers.
Advanced: custom tiers
Three tiers not enough? Tick Advanced (custom tiers) and write your own as JSON.
[ { "label": "Exceptional", "color": "#15803d", "match": { "op": "gte", "value": 120 } }, { "label": "On Target", "color": "#22c55e", "match": { "op": "gte", "value": 100 } }, { "label": "Near Target", "color": "#84cc16", "match": { "op": "gte", "value": 95 } }, { "label": "Behind", "color": "#f59e0b", "match": { "op": "gte", "value": 80 } }, { "label": "Critical", "color": "#991b1b", "match": { "op": "default" } }]
The rules:
First match wins. Order matters. Put your highest band first.
Every tier needs a
labeland amatch.Operators:
gt,gte,lt,lte,eq,between(withminandmax, both inclusive), anddefault.At most one
default— it catches anything the others miss. Without one, a value matching nothing shows no status.Colours are the text colour of the word.
The tiers are measured in whatever Measured as says. Paste the array above with Measured as: Percent of a reference and Compare to: The goal, and you have a five-band progress indicator against target.
The editor validates as you type and tells you what's wrong. If the JSON is invalid, no tiers are saved — the message under the box is your warning.
Flag unusual values
An independent alarm. Ticking it adds a ⚠ chip when the current value sits more than Sensitivity standard deviations from the average of the trend. Needs at least three periods.
This is separate from Standard deviations from average under Measured as. One flags an outlier; the other colours the status word. Use either, or both.
8. Goal Options
A target beneath the value, with an arrow showing which side of it you're on.
Pick a Goal Type:
Goal Type | The target is |
|---|---|
Static value | a number you type — |
Report value | a cell — pick a column and a row |
Per period | a column, aggregated over the same period as the value |
Per period is the one worth understanding. If your report has a budget column beside revenue, choose it as the Target Column and March's actuals will meet March's budget — not the year's. Switch the card to quarterly and the target follows. It only appears when the Value Source is period-based, because a per-period target against a single cell means nothing.
Heading is the word before the target. It defaults to Goal; type Target or Budget if you prefer. Prefix and suffix labels sit either side of the number.
Direction — Higher is good, Higher is bad, or None. This colours the arrow. The arrow character always points at where the value sits relative to the target; the colour says whether that's good news.
A goal of zero is a real goal, not a missing one — the card shows it and the arrow works. But "percent of target" against a target of zero has no meaning, so no status word appears.
9. Caption & Subtitle
Caption is a line of context under the value. It's the most under-used feature on the card.
Type @ and a picker appears. Choose a token; the card fills it in at run time.
Token | Becomes |
|---|---|
| the formatted value |
| the comparison's prior figure |
| the absolute change |
| the percentage change |
| the current period's label — |
| the prior period's label |
| the current period's date range |
| the number of rows behind the value |
| the formatted target |
| percent of target |
| the distance to target |
| any column's value from the first row |
So vs {prior} in {priorPeriod} renders as vs $565,921 in Feb 2026, and {goalPct} of {goal} — {toGoal} to go renders as 82% of $3,000,000 — $540,000 to go.
Tokens that can't be resolved render as nothing. {goalPct} against a target of zero stays blank; {toGoal} still works, because subtraction survives a zero where division doesn't.
Subtitle is a plain line beneath, for context that never changes.
10. Interactivity
Drill-down
Set a Target Report and the card becomes clickable. Clicking the big number opens that report, filtered to the rows behind the number.
For a period-based card that means the exact date range of the period you clicked — click March, get March. Click a bar in the trend and you get that bar's period. Click the change chip and you get the prior period.
For a single-cell, text, or total card there's no date window to pass, so the target inherits the source report's own filters instead. That is still the right answer: the rows behind the number.
Map period to target date field tells the card which field in the target report to filter. The target must have the same underlying database date field for a date range to carry across.
If the trend is on, click-through on individual points can be enabled separately.
Viewer Controls
Let people who read the dashboard change what the card shows, without editing it. Nothing is saved — the card returns to your settings on refresh.
"Let viewers switch the period grain" — adds a small pill to the corner of the card. Use Periods offered to choose which options appear. Only periods at or coarser than your data are offered; the card can't invent daily detail from monthly rows.
"Let viewers switch comparison basis" — lets them flip between previous period and same period last year.
Neither appears for year-less period labels, because neither can be calculated.
11. Performance: keeping a dashboard of KPI cards fast
Everything above is about capability. This is about speed, and it's the part that decides whether your dashboard opens in one second or fifteen.
11.1 Why it matters
Every KPI tile runs its own report, and therefore its own database query.
The card does its arithmetic — the totals, the comparisons, the trend — on whatever rows the report hands back. So the cost of a card is the cost of its query and the size of its result.
Ten tiles, each pulling five years of transaction rows, is ten heavy queries every time anyone opens the dashboard. The card only displays one number. The database doesn't know that.
Three levers, cheapest first.
11.2 Constrain the dates
Most KPI cards answer a question about now. They rarely need every row ever recorded.
Add a criterion on your date field and use one of the predetermined ranges — Year to Date, Last 3 Months, Month to Date. Your data keeps growing; your query doesn't.
If you need year-over-year, you need this year and last year — nothing more. Two criteria on the same field:
order_date Is Year to DateOrorder_date Is Last Year
Set the Logical Filter on the second criterion to Or. (The first criterion in a report is always AND; only later ones expose OR.) The report now returns two years instead of five, and Same period last year still works perfectly.
See Criteria and prompts.
11.3 Filter to the subset that matters
If the card is about open tickets, filter to open tickets.
status Is Open
Every row nobody is looking at is a row the database read, sent over the network, and the browser parsed, for nothing. This sounds obvious. It is routinely skipped, because the report was built for something else and then reused.
Anything that narrows the data to what the card is actually about is worth doing: a region, a product line, a status, an active flag.
Keep the filter column non-visible — see 11.6.
11.4 Aggregate when you don't need the fine detail
The biggest win, and the most under-used.
A card showing a monthly trend does not need 240,000 daily transaction rows. It needs 24 monthly totals. Do that aggregation once, in the report, instead of shipping every row to the browser.
Add Formula on your date column to produce a period label. In the formula box type
@, choose DateTime, and use Insert Report Field to drop in your date. Produce a value like2026-03.Add Formula on your value column, choose Aggregate, and wrap it in
Sum.Remove — or set non-visible — every other column that would split the rows apart.
Your report now returns one row per month. The card behaves identically and loads in a fraction of the time.
Keep the year in the period label.
2026-03orMar-2026, neverMarch. A year-less label drops you to rung two of the ladder in section 1 and you lose year-over-year — the very comparison you were probably aggregating in order to afford.
See Report formulas and Data prep tools.
11.5 What aggregating costs you
You keep | You give up |
|---|---|
Monthly, quarterly, yearly figures | Daily and weekly detail |
Year-over-year and period-to-date | Viewer period switching below your chosen period |
The trend chart | Click-through to an exact date range |
Goals, thresholds, comparisons |
That last one deserves a sentence. When you aggregate your dates into a text period column, click-through still works — it just carries the report's own filters rather than the exact period you clicked. If drilling into the rows behind a single month matters more than load time, keep the raw date column and constrain by date instead.
11.6 Don't let filter columns fragment your rows
The most common reason an aggregated report still comes back huge.
You aggregate to one row per month. Then you leave customer_name in the report because you want to filter on it. Now you have one row per month per customer, and you're back to thousands of rows.
A field can be used in criteria while being non-visible. Turn the column off. The filter still works. The rows stay collapsed.
Chart data shaping calls this the single biggest cause of messy data, and it's right. Dataset prep covers the Visible toggle.
11.7 When to stop fighting the report
If the shaping you need can't be expressed with criteria, formulas, and non-visible columns, stop.
That's the signal to move up a level: a database view or Direct SQL query, a stored procedure, surfaced through a Virtual App. Aggregating three million rows belongs in the database, which is built for it — not in a report that has to do it on every dashboard load.
The shaping belongs in the data.
12. Three worked examples
One number, no period
region | revenue |
|---|---|
North | 412000 |
South | 388000 |
Value Source: Single value (cell) · Comparison Source: vs another row. A card showing North's revenue, with the change against South.
A trend and one comparison
month | revenue |
|---|---|
January | 310000 |
February | 344000 |
March | 366000 |
Value Source: Latest period · Comparison: vs previous period · Trend: Bars. "366,000 — up 6.4% on the period before." Add a year to month and everything in section 5 opens up.
The full card
order_date | revenue | budget |
|---|---|---|
2026-01-14 | 12400 | 15000 |
2026-01-27 | 9800 | 15000 |
2026-02-03 | 15200 | 16000 |
Value Source: Latest period · Group by: Month · Calculation: Sum Comparison: vs a period → Same period last year Goal: Per period → budget Status: Measured as Percent of a reference, Compare to The goal, Good at 100, Bad at 90 Caption: {goalPct} of {goal} — {toGoal} to go Interactivity: Drill-down to a transaction report
March's revenue, against March last year, against March's budget, with a status word, a trend, and a click straight through to March's transactions.
13. Common pitfalls
Symptom | Cause | Fix |
|---|---|---|
My date column isn't in the Period Column list | Its values aren't consistently readable as dates — mixed formats, too many blanks, or a date buried in free text | Clean the column, or build one consistent formula column |
No Same period last year option | Your period labels have no year |
|
No Group by option | A year-less period column can't be rolled up | Add the year |
No Trend option | Value Source isn't period-based, or the report has no readable date | Switch to Latest period |
Dates read as the wrong day |
| Use ISO: |
My number column won't Sum | It's typed as text | Override the data type |
Trend is a solid block of bars | Too many periods | Coarsen Group by, or aggregate the report |
Status word never appears | The reference can't be resolved — no goal, a goal of zero, or no prior period | Check Compare to in Status & Thresholds |
The status block vanished entirely | Invalid custom-tier JSON | Read the message under the box |
Change chip is green when the news is bad | Favorable is wrong | Set Lower is better |
A 20%→25% rise reads as +25% | Change as is Percent | Set Percentage points, or Auto |
Click-through ignores the period I clicked | The period column is text, not a real date | Keep the raw date column and constrain by date |
Dashboard takes forever to load | Every tile queries years of raw rows | Sections 11.2–11.4 |
I aggregated and the row count is still huge | A filter column is splitting the rows | Set it non-visible (11.6) |
14. Checklist
Before you build a card
[ ] Does the report have a period column in one consistent format?
[ ] Do the period labels carry a year? (
Mar-2026, notMarch)[ ] Is the value column typed as a number?
[ ] For a per-period target, is the target column in the same report?
Before you configure it
[ ] Is Value Source right? Latest period unlocks comparisons, trends, and per-period goals.
[ ] Is Favorable set the right way round?
[ ] Does the status reference actually exist — a goal, a prior period, three periods of data?
Before you put six of them on a dashboard
[ ] Is every card constrained by date? (Year to Date, or Year to Date Or Last Year)
[ ] Is every card filtered to the subset it's about?
[ ] If you don't need daily detail, is the report aggregated to the period you display?
[ ] Are filter-only columns set non-visible?
The card will work without any of this. Your dashboard will be quicker with all of it.
Related
How to Structure Your Data for the Financial Report Visualization — the opposite philosophy: a visualization with a strict data contract