Wallet home screen

ProReact Native · Expo

A banking or wallet home: greeting, the balance of the centered card with hide and show, a swipeable card carousel, quick actions, this month's spending as a scrubbable sparkline and recent transactions, all inside pull to refresh. Swiping to another card rolls the balance, morphs the sparkline and swaps the list.

View as Markdown
Try it on your phone
Scan to open this demo in your phone's browser and use it with your fingers.

Installation

$ npx shadcn@latest add @goodui-native-pro/wallet-home-screen

Then run npx expo install --fix so react-native-svg match your Expo SDK. Files land in components/goodui-native/wallet-home-screen.

Pro screens install from the license-gated @goodui-native-pro registry. Setup: Pro access.

  • Wrap the app in GestureHandlerRootView and ThemeProvider.

Usage

// app/(tabs)/index.tsx
import { Stack, router } from "expo-router";
import { WalletHomeScreen } from "@/components/goodui-native/wallet-home-screen/wallet-home-screen";
import { useAccounts } from "@/lib/bank";

export default function HomeRoute() {
  const { user, cards, transactions, refetch, unread } = useAccounts(); // your data source
  return (
    <>
      <Stack.Screen options={{ headerShown: false }} />
      <WalletHomeScreen
        userName={user.firstName}
        avatar={user.photoUrl}
        cards={cards} // [{ id, name, last4, balance, currency: "EUR", spending: [daily totals] }]
        transactions={transactions} // newest first, amount negative for spending, cardId to filter
        onRefresh={refetch} // return the promise; new balances roll in when it resolves
        onQuickAction={(action, card) => router.push(`/${action}?card=${card.id}`)}
        onTransactionPress={(t) => router.push(`/transaction/${t.id}`)}
        onSeeAllTransactions={() => router.push("/transactions")}
        onNotificationsPress={() => router.push("/notifications")}
        notificationCount={unread}
        onProfilePress={() => router.push("/profile")}
      />
    </>
  );
}

When to use

Use it for
  • The home tab of a bank, wallet, card or budgeting app with one or more cards or accounts.
  • Any money app where the balance, a quick send and the latest payments are the first things people look for.
Reach for something else
  • A full transaction history with search and filters: open your own list from onSeeAllTransactions; this screen shows a short recent list.
  • Investment portfolios with holdings and charts per asset: compose sparkline and list-row yourself.
  • One transaction's details: use transaction-detail-screen.

Examples

On its own, with sample cards and transactions

<WalletHomeScreen />

Amounts hidden by default, remembered by the app

<WalletHomeScreen cards={cards} transactions={transactions} balanceHidden={hidden} onBalanceHiddenChange={setHidden} onRefresh={refetch} />

API reference

PropTypeDefault
userName
First name for the greeting.
string"Maya"
avatar
Profile photo. Without it the avatar shows initials.
ImageSourcePropType | string—
greeting
Greeting above the name. Defaults to good morning, afternoon or evening by the device clock.
string—
cards
Cards and accounts, one carousel card each. The balance, sparkline and transactions follow the centered card.
WalletCard[]defaultCards
transactions
Recent transactions, newest first. Filtered to the centered card by `cardId`.
WalletTransaction[]defaultTransactions
cardIndex
Index of the centered card (controlled).
number—
onCardIndexChange
Called when a different card settles in the center.
(index: number, card: WalletCard) => void—
balanceHidden
Hide amounts (controlled).
boolean—
defaultBalanceHidden
Start with amounts hidden when uncontrolled.
booleanfalse
onBalanceHiddenChange
Called when the eye button hides or shows amounts.
(hidden: boolean) => void—
onRefresh
Pull to refresh. Return a promise; pass new `cards` and `transactions` when it resolves and the balance rolls to them.
() => unknown() => new Promise((resolve) => setTimeout(resolve, 900))
refreshing
Show the refreshing state from outside, for example on first load.
boolean—
onQuickAction
Send, request, top up or more, with the centered card. Return a promise to show a spinner on the button.
(action: WalletQuickAction, card: WalletCard) => unknown—
onCardPress
Tap on the centered card.
(card: WalletCard) => void—
onTransactionPress
Tap on a transaction row, for example to open its detail screen.
(transaction: WalletTransaction) => void—
onSeeAllTransactions
"See all" next to the transactions heading. Hidden when omitted.
() => void—
onNotificationsPress
The bell in the header. Hidden when omitted.
() => void—
notificationCount
Count on the bell.
number—
onProfilePress
Tap on the avatar.
() => void—
locale
BCP 47 locale for amounts and dates. Defaults to the device locale.
string—
transactionLimit
How many transactions to list. Default 6.
number6
style
Style for the outer container.
StyleProp<ViewStyle>—
testID
Identifier for end-to-end tests.
string—

Motion and haptics

  • Swiping the card carousel rolls the balance and the month total to the new card's values, morphs the sparkline into the new card's spending and swaps the transaction list.
  • Pull to refresh: the drop stretches, snaps round past the threshold, spins while onRefresh runs, and when new cards arrive the balance digits roll in the direction of the change.
  • Scrubbing the sparkline rolls the total to that day's amount and the label switches to 'Spent on <date>'.
  • Reduced motion: digits change in place, the carousel settles on a short timing curve instead of a spring, the sparkline changes at once without drawing or morphing (each component's own reduced-motion branch).
  • Haptic: select when a swipe settles on another card (Carousel)
  • Haptic: tap when the pull passes the threshold (PullToRefresh)
  • Haptic: select on the eye toggle, tap on quick actions and the bell (IconButton)
  • Haptic: select for each day the sparkline scrub crosses (Sparkline)
  • Gestures, the roll and the sparkline animate on the UI thread; React renders on card changes, scrubbed day changes, refreshes and the eye toggle.
  • The transaction list is limited by transactionLimit (6) and rendered in a ScrollView; page the full history on its own screen.

Accessibility

  • The greeting name is a header; the avatar and bell are buttons with labels ('Notifications, 3 new').
  • The eye is a button labelled 'Hide amounts' or 'Show amounts'; toggling it announces 'Amounts hidden' or 'Amounts shown'. Hidden amounts are read as 'Balance hidden'.
  • The card carousel is one adjustable element: swipe up or down (arrow keys on the web) to change card; its value reads 'Card 2 of 3, Savings card ending 0019'.
  • Quick actions are icon buttons named by their visible label; the label under each is hidden from screen readers so it is not read twice.
  • Each transaction row is read as 'Merchant, signed amount, category, date or status'. The balance and totals are read as plain numbers (rolling-number).
  • Pull to refresh has a Refresh button for screen readers on every platform.

Notes for AI

  • Pass cards with balance as a number and currency as an ISO code; amounts are formatted with Intl in the given locale. spending holds daily totals oldest first and drives the sparkline and 'Spent this month'.
  • Transactions: amount is signed (negative for spending), date is an ISO string or Date, cardId ties a row to a card; rows without cardId show under every card.
  • Return the promise from onRefresh and update cards and transactions when it resolves; without onRefresh the pull waits 0.9 s and resolves, which is only useful in a preview.
  • Header buttons appear only for the callbacks you pass: onNotificationsPress (bell), onSeeAllTransactions (See all). onProfilePress makes the avatar a button.
  • Render it full-bleed with the navigator header hidden (headerShown: false); it handles the safe areas itself. Do not nest it inside another ScrollView: it is the screen's scroll view.