- Accordion
- Action Bar
- Action Toast
- Alert Banner
- Alert Dialog
- Alert
- Alphabet Index
- Amount Field
- Aspect Ratio
- Attachment
- Availability List
- Avatar
- Badge
- Bank Card
- Breadcrumb
- Bubble
- Button Group
- Button
- Calendar
- Camera Frame
- Card Design Picker
- Card Details
- Card Peek
- Card Stack
- Card
- Carousel
- Chart
- Chat Thread
- Checkbox
- Chip Group
- Choice Tile
- Code Block
- Code Dots
- Collapsible
- Combobox
- Command
- Context Menu
- Copy Field
- Countdown
- Country Select
- Data Table
- Date Field
- Date Picker
- Date Range Picker
- Dialog
- Direction
- Document Paper
- Drawer
- Dropdown Menu
- Empty
- Entity Avatar
- Field
- File Trigger
- File Upload Item
- Hover Card
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Key Value
- Keypad
- Label
- Live Region
- Marker
- Message Scroller
- Message
- Money Amount
- Native Select
- Number Field
- Pagination
- Percent Split Editor
- Phone Field
- Popover
- Progress
- QR Code
- Questionnaire
- Radio Group
- Range Histogram
- Rating
- Resizable
- Scroll Area
- Search Field
- Select
- Separator
- Share Bar
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Sortable List
- Spinner
- Status Timeline
- Step Header
- Steps List
- Swipe Row
- Switch
- Table
- Tabs
- Text Field
- Textarea
- Tier Ladder
- Toggle Group
- Toggle
- Token Search Field
- Tooltip
- Trace
- Transaction Row
- Usage Meter
Today
Total -$27.90−$27.90Yesterday
Total +$3,175.00+$3,175.00"use client"
import {The TransactionRow component shows one movement of money: who or what it was, the time and context with an inline status, and a signed amount. It has no leading icon. Group rows by day or state with TransactionGroup. Use it for activity feeds and history lists; for a row with media and actions, use Item, and for many columns, use a table.
Installation#
pnpm dlx shadcn@latest add @dots/transaction-row
Usage#
import {
TransactionGroup,
TransactionRow,
} from "@/components/ui/transaction-row"<TransactionGroup label="Today" total={-27.9}>
<TransactionRow
title="Corner market"
description="14:32 · Groceries"
amount={-23.4}
/>
<TransactionRow
title="Coffee Point"
description="08:14 · Eating out"
amount={-4.5}
/>
</TransactionGroup>Composition#
Use the following composition to build a list of transactions:
TransactionGroup
├── TransactionRow
└── TransactionRowWhile the list loads, render the skeletons in its place:
TransactionGroupSkeleton
└── TransactionRowSkeletonFeatures#
- Signed amount through
MoneyAmount, with the sign taken fromamountor set bydirection - Five statuses:
completedshows no marker,pendingandrefundedadd a quiet one,failedandcancelledalso strike the amount - A plain row, a link with
href, or a button withonPress, each as one pressable element maskedhides the digits when the user hides their balance- A group heading with an optional total, or a hidden heading, or an outside heading
- Skeletons on the row's own metrics for loading states
Statuses#
Set status to show where the transaction stands. The marker follows the description.
"use client"
import { TransactionRow } from "@/components/ui/transaction-row"Direction#
A negative amount is money out and a positive one is money in. Set direction when your data stores amounts without a sign.
"use client"
import { TransactionRow } from "@/components/ui/transaction-row"Pressable rows#
Pass href to make the row a link, or onPress to make it a button that opens a details sheet. isDisabled works on both.
Press a row to open its details.
"use client"
import * as React from "react"Group labels#
Inside a titled section, pass the title's id as aria-labelledby instead of label. Use hideLabel when the screen title already says what the list is.
Recent card payments
- Corner market14:32 · Groceries-$23.40−$23.40
- Coffee Point08:14 · Eating out-$4.50−$4.50
Upcoming payments
- Rent1 Dec · Scheduled·, Pending-$1,450.00−$1,450.00
"use client"
import * as React from "react"Masked#
Set masked on the rows and the group to replace the digits with dots.
Today
Total Amount hidden$••••- Corner market14:32 · GroceriesAmount hidden$••••
- Coffee Point08:14 · Eating outAmount hidden$••••
"use client"
import {Loading#
Use TransactionGroupSkeleton and TransactionRowSkeleton while the data loads. Set media when the real rows lead with an avatar.
"use client"
import {Accessibility#
A row with href renders a React Aria Link and a row with onPress a React Aria Button, so the whole row is one focusable element with press, keyboard and disabled handling. Without either, it is plain content.
Row name#
A pressable row is named by its text: title, description, status and amount, in that order. A comma is read between the description and the status. A static row has no role or name of its own; its text is read as plain content in the same order. Keep the title short and specific, such as a merchant or a person's name.
Groups#
TransactionGroup renders a list named by its heading, and wraps each child in a list item. The heading is an h3 by default; set headingLevel to fit the page outline. The total is read with totalLabel before it, "Total" by default.
Loading#
The skeletons are hidden from assistive tech. Mark the loading container with aria-busy="true" and announce loading once, not per row, with a role="status" element outside it.
Meaning beyond color#
failed and cancelled strike the amount, and every status other than completed is also written as text.
API Reference#
TransactionRow#
Without href or onPress it accepts all props of <div>. With href it accepts React Aria Link props, and with onPress React Aria Button props.
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | - | Who or what the money went to or came from. Required. |
description | ReactNode | - | Time and context. The status marker follows it. |
amount | number | - | Amount in major units. Negative is money out. Required. |
currency | string | "USD" | ISO 4217 code. |
locale | string | "en-US" | Locale for the amount. |
status | "completed" | "pending" | "failed" | "cancelled" | "refunded" | "completed" | Where the transaction stands. |
direction | "in" | "out" | - | Overrides the sign of amount. |
masked | boolean | false | Replace the digits with dots. |
href | string | - | Makes the whole row one link. |
onPress | (e: PressEvent) => void | - | Makes the whole row one button. Not with href. |
className | string | - | Additional classes to apply to the row. |
TransactionGroup#
A labelled list of rows. Accepts all props of <div> except title. Pass either label or aria-labelledby.
| Prop | Type | Default | Description |
|---|---|---|---|
label | ReactNode | - | What the rows share, such as "Today". Rendered as a heading. |
hideLabel | boolean | false | Keeps the heading for screen readers only. |
aria-labelledby | string | - | Id of an outside heading that names the list. Not with label. |
total | number | - | Sum for the group, shown next to the label. |
currency | string | "USD" | ISO 4217 code for the total. |
locale | string | "en-US" | Locale for the total. |
totalLabel | string | "Total" | Read before the total by screen readers. |
masked | boolean | false | Replace the total's digits with dots. |
headingLevel | 2 | 3 | 4 | 5 | 6 | 3 | Heading level of the label. |
TransactionRowSkeleton#
A placeholder with the row's metrics. Accepts all props of <div> except children.
| Prop | Type | Default | Description |
|---|---|---|---|
media | boolean | "circle" | "square" | false | A leading placeholder for rows with an avatar. true is a circle. |
TransactionGroupSkeleton#
A placeholder for a whole group. Accepts all props of <div> except children.
| Prop | Type | Default | Description |
|---|---|---|---|
rows | number | 3 | How many row placeholders to show. |
total | boolean | false | Adds a placeholder for the total. |
media | boolean | "circle" | "square" | false | Passed to each row placeholder. |