Skip to content
All writing

The Home dashboard: one screen that earns the default tab

5 min read

reactnative
mobile
ux
buildinpublic

For most of this series, the app's default tab was a 12-line placeholder that said "Welcome to CannyCart." Not laziness — a real problem. The other tabs each own something: Shopping owns the list, Expenses owns the money, More owns settings. Home owned nothing, and a screen that owns nothing has two fates: stay empty, or become the drawer where every feature idea gets dumped.

The way out was to give Home a job rather than content: answer "what now?" — the state of the current shop and the fastest way to act on it. And then to write two rules strict enough that the job couldn't sprawl.

TL;DR — Home shipped in one commit as pure composition of hooks the other tabs already own: no backend, no schema, no rebuild. Two rules kept it honest: Home links into the tabs, it never reimplements them (every card is a doorway with a number on it), and a card earns its place only if it changes what you do next (so: no streaks, no counts, no charts). Capture lives inside the shop card, under the list it will add to. The one card that creates — "Add these N again" from the last receipt — promises exactly the N it will add. "This month" compares calendar months, and a rise from £0 shows no percentage, because dividing by zero is infinite, not "up 100%".

(Part 26 of Building CannyCart, a voice-first shopping app I'm building in public. Self-contained — no earlier context needed.)

Two rules, both load-bearing

Rule 1: Home links into the tabs; it never reimplements them. Every card is a doorway with a number on it. The moment a card becomes editable, it has started competing with the tab that owns that data — two places to change the budget, two places that can disagree. Home reports and routes; the tabs do.

Rule 2: a card earns its place only if it changes what you do next. This is the test every proposed card has to pass. "£128 this month, 12% less than last" passes — you might loosen up or tighten the budget. "You've logged 47 receipts" fails — it's a fact with no next action. That test is why Home has no streaks, no counts, no scores, and no charts (Trends owns charts). Most dashboard clutter is cards that would fail Rule 2 if anyone asked.

The cards, in order

Greeting · shop card · unpriced strip · this month · a saving · last shop · buy again. A few of the placements carry real decisions.

The shop card holds progress, money, and capture. Items left to get and money left to spend are the same question asked twice, so they share a card. More importantly, the capture buttons (Speak / List / Barcode) live inside the card, directly under the list name they'll add to. "Which list am I adding to?" is answered structurally — by where the buttons are — rather than by a label you'd have to notice. The list name carries a chevron and opens the list switcher, so you switch before capturing instead of discovering the mistake afterwards.

"This month" is calendar month vs. the previous one — deliberately not the rolling-window total the Trends screen computes. People think in months, not trailing 90-day windows. And one honesty rule inside it: a rise from a £0 month shows no percentage at all. Dividing by zero is infinite, not "up 100%," and a dashboard that prints "▲ ∞%" has stopped being a dashboard.

A saving worth acting on — the single coral element on Home, and only when it's real: the same gated selector as the Savings screen (fresh, per-store, two distinct prices). No real saving → the card is absent entirely, never a "£0 saved" placeholder. Home can't show a fake deal by construction.

Buy again — the frequently-bought chips from Part 10, the cheapest possible way to build a list, one tap each.

The Home tab: the shop card with capture buttons inside it, this month's total, the last shop with 'Add these again', and buy-again chips — every card a doorway

The one card that creates

Everything above reports. One card creates: the last receipt — store, date, total — with an "Add these N again" button that turns receipt lines back into list items. It closes the loop between Expenses and Shopping: last week's receipt becomes next week's list in one tap, from data that had been sitting in the receipt snapshots since Part 14.

The detail that makes it trustworthy: N is what would actually be added. Items already sitting unchecked on the list are skipped, and repeats within the receipt itself collapse — so the button never promises 12 and adds 3. A button that says 3 and adds 3 is worth more than a bigger number that lies. (The same machinery later powered "Add these again" on the purchase-history screen.)

Strips, not cards

Two things on Home are deliberately thin rows, not cards: the offline sync banner (queued changes waiting for signal) and "N items need a price" (linking to receipt scanning). They're status notices, not destinations — giving them card weight would push real content off the first screen. Visual weight is an argument about importance; status shouldn't win it.

Empty states are the first impression

A new user's Home is the first screen they ever see, so every card has an honest empty state instead of a wall of zeros:

  • Empty list → a welcome with the three capture buttons front and centre (say it, photograph it, scan it), not "0 of 0 items".
  • No budget → an offer to set one, not "£0 of £0".
  • No real saving → nothing. No card, no placeholder.

A dashboard full of zeros teaches new users that the app has nothing for them. An honest empty state teaches them what to do.

It was all already built

The reuse map from the plan says it best — every card maps to hooks and components the tabs already own:

Card Source
Shop card active-list hook · items hook · expenses selector · the list switcher sheet
Capture the voice sheet · the photo sheet · the scanner route
This month the trends hook
Saving useSavings().best
Last shop + repeat the receipts hook · receipt lines · addItems
Buy again the frequent-items hook + chips

No backend, no schema, no rebuild. Home was the payoff for eighteen parts of building features as hooks the screens compose rather than screens that own their data. When the dashboard finally arrived, it was assembly.

What I took away

  • Give the default tab a job, not content. "What now?" is a job; "welcome" is a placeholder.
  • Doorways, not dashboards. A card that can be edited competes with the tab that owns the data.
  • Every card must change what you do next. Streaks, counts and vanity charts fail the test; delete them before they ship.
  • Answer "which list?" structurally. Put the action under the thing it acts on.
  • Never promise a number you won't deliver — N is the count that will actually be added.
  • ∞% is not a percentage. Handle the zero baseline with silence.

Next up

Part 27 is the second polish montage: a real Settings page, keep-awake in the aisle, the one-default-list race, the Android back button, and the platform-asymmetric tab inset that finally stopped content hiding under the tab bar.

What's on your app's home screen that would fail the "does it change what you do next?" test?


0 reactions · 0 comments

Discuss on dev.to