Wallet home screen
ProReact Native · ExpoA 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.
Installation
$ npx shadcn@latest add @goodui-native-pro/wallet-home-screenThen 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
- 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.
- 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
| Prop | Type | Default |
|---|---|---|
| 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. | boolean | false |
| 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. | number | 6 |
| 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.
Related
One payment in full: merchant header, the signed amount, a status badge and a category chip that recategorizes, the details as grouped rows, split and receipt actions and Report a problem. The split sheet rolls each person's share as you change the head count.
A snapping card carousel that follows the finger 1:1, shrinks and dims the neighbours, stretches its pagination pill with the drag, and turns that pill into a timer when it autoplays.
A number whose digits roll in the direction of change, with digits that open and close in place and currency signs that glide instead of jumping.
A small line or area chart that draws itself in and morphs to new data; press and drag to scrub, and a dot rides the curve while the line beyond your finger dims and every point ticks.
A scroll view whose refresh indicator is a drop that stretches as you pull, snaps round with a bounce and a haptic past the threshold, spins while onRefresh runs and ends on a check or an error.
Settings and navigation cells with a token-colored icon tile, title, subtitle and a trailing value, switch, badge, check or chevron, grouped in inset sections with headers and footers. On touch down the highlight floods in, the tile sinks and the chevron leans forward; on release the highlight lingers and fades.