Overview
A ListCell row is divided into the following 5 columns:
- Media
- Title & description
- Intermediary
- End (detail & subdetail or action)
- Accessory
Basic Usage
<ListCell
spacingVariant="condensed"
title="Basic List Cell"
description="A simple example of ListCell"
/>
TipPrefer spacingVariant="condensed" for the new ListCell design. The compact may be removed in a future major release.
Spacing Variant
<VStack>
{}
<ListCell
accessory="arrow"
description="New design (condensed)"
detail="$12,345.00"
spacingVariant="condensed"
media={<Avatar src={assets.eth.imageUrl} size="m" />}
onClick={console.log}
title="Condensed"
variant="positive"
/>
{}
<ListCell
accessory="arrow"
detail="$12,345.00"
spacingVariant="compact"
media={<Avatar src={assets.eth.imageUrl} size="m" />}
onClick={console.log}
title="Compact (deprecated)"
variant="positive"
/>
<ListCell
accessory="arrow"
detail="$12,345.00"
spacingVariant="normal"
media={<Avatar src={assets.eth.imageUrl} size="m" />}
onClick={console.log}
title="Normal"
variant="positive"
/>
</VStack>
NoteWe have deprecated CellMedia; pass media directly as shown below.
Leading Icon
<ListCell
spacingVariant="condensed"
title="List Cell with Icon"
description="Shows usage with a leading icon"
media={<Icon active name="info" />}
/>
Leading Avatar
<ListCell
spacingVariant="condensed"
title="List Cell with Icon"
description="Shows usage with a leading icon"
media={<Avatar src={assets.btc.imageUrl} size="l" />}
/>
Title & Description
Title Line Limits
- In condensed spacing (
spacingVariant="condensed"), the title shows up to two lines by default, regardless of whether a description is present.
- In normal and compact spacing, the title shows up to two lines when there is no description; if a description is present, the title is limited to one line.
- Use
disableMultilineTitle to force the title to one line in all cases.
WarningThe title and description props are rendered inside a CDS Text with default fonts and truncation. To render arbitrary React nodes without being wrapped by a <Text>, use titleNode and descriptionNode.
When using the Node props, you are responsible for styling, layout, and truncation behavior.
Custom Title/Description via Node Props
<ListCell
spacingVariant="condensed"
media={<Avatar src={assets.eth.imageUrl} size="m" />}
titleNode={
<HStack gap={1} alignItems="center">
<Icon name="checkmark" />
<span>Verified account</span>
</HStack>
}
descriptionNode={
<HStack gap={1} alignItems="center">
<span>Composed description with any React nodes</span>
<Icon name="info" />
</HStack>
}
/>
Multiline Description
<ListCell
spacingVariant="condensed"
title="Multiline Description"
description="This is a longer description that demonstrates how the text wraps when the multiline prop is enabled. It can span multiple lines without truncating."
multiline
/>
function Intermediary() {
const dimensions = { width: 62, height: 18 };
const sparklineData = prices
.map((price) => parseFloat(price))
.filter((price, index) => index % 10 === 0);
const referenceY = sparklineData[Math.floor(sparklineData.length / 3)];
const CompactChart = memo(
({ data, color = 'var(--color-fgPositive)', showArea = false, referenceY }) => (
<Box style={{ padding: 1 }}>
<LineChart
{...dimensions}
enableScrubbing={false}
overflow="visible"
inset={0}
showArea={showArea}
series={[
{
id: 'series',
data,
color,
},
]}
>
<ReferenceLine dataY={referenceY} />
</LineChart>
</Box>
),
);
return (
<ListCell
media={<Avatar src={assets.btc.imageUrl} />}
spacingVariant="condensed"
title="Bitcoin"
description="BTC"
intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
detail="$334,239.03"
subdetail="+4.06%"
priority="start"
variant="positive"
/>
);
}
End
Detail and Subdetail
<ListCell
spacingVariant="condensed"
title="List Cell with Details"
description="Shows usage with detail and subdetail"
detail="Primary detail"
subdetail="Secondary detail"
/>
WarningLike title and description, detail and subdetail props are also rendered inside a CDS Text with default fonts. To render arbitrary React nodes without being wrapped by a <Text>, use detailNode and subdetailNode.
Custom Detail/Subdetail via Node Props
<ListCell
spacingVariant="condensed"
media={<Avatar src={assets.btc.imageUrl} size="m" />}
title="Custom end content"
description="Detail and subdetail rendered with custom nodes"
detailNode={
<HStack gap={2} alignItems="center" justifyContent="flex-end">
<Icon name="info" />
<Text as="div" font="body" overflow="truncate" textAlign="end">
$12,345.00
</Text>
</HStack>
}
subdetailNode={
<HStack gap={1} alignItems="center" justifyContent="flex-end">
<Icon name="info" />
<Text as="div" color="fgPositive" font="label2" overflow="truncate" textAlign="end">
+5.43%
</Text>
</HStack>
}
/>
End Action
When you pass the end prop, it overrides the detail/subdetail/detailNode/subdetailNode.
<ListCell
spacingVariant="condensed"
title="End action"
detail="This is overridden and won't show up"
subdetail="This is overridden and won't show up"
detailNode="This is overridden and won't show up"
subdetailNode="This is overridden and won't show up"
end={
<Button
compact
onClick={() => {
alert('Action clicked');
}}
>
Action
</Button>
}
/>
Accessory
Interactive Cell with Accessory
<ListCell
spacingVariant="condensed"
title="Interactive List Cell"
description="Click or tap to interact"
accessory="arrow"
onClick={() => alert('Cell clicked!')}
/>
Custom Accessory via Node Prop
<ListCell
spacingVariant="condensed"
title="Accessory Node"
description="Custom accessory with its own onClick"
media={<Avatar src={assets.eth.imageUrl} size="m" />}
end={
<Button
compact
onClick={() => {
alert('Action clicked');
}}
>
Action
</Button>
}
accessoryNode={
<Tooltip content="question">
<Icon size="s" name="questionMark" compact variant="secondary" />
</Tooltip>
}
/>
Accessibility Label
The accessibility props are only applied when the <ListCell> has a value for the onClick prop. Otherwise, content passed into the <ListCell> must use accessibility props and attributes as needed.
<VStack gap={1}>
<ListCell
accessibilityLabel="Accessibility label. Describes content for entire list cell. Applied when onClick prop has a value"
intermediary={<Icon name="chartLine" />}
media={<Avatar src={assets.btc.imageUrl} />}
onClick={() => window.alert('ListCell clicked!')}
title="BTC"
spacingVariant="condensed"
/>
<ListCell
intermediary={<Icon accessibilityLabel="Chart icon" name="chartLine" />}
media={<Avatar accessibilityLabel="Bitcoin" src={assets.btc.imageUrl} />}
title="BTC"
spacingVariant="condensed"
/>
</VStack>
Helper text
<VStack gap={3}>
<ListCell
spacingVariant="condensed"
title="List Cell with Helper Text"
description="Shows usage with helper text below the cell"
helperText={
<CellHelperText font="label2" paddingStart={6}>
This is a default helper message.
</CellHelperText>
}
media={<Avatar src={assets.btc.imageUrl} />}
end={<Button compact>Action</Button>}
/>
<ListCell
spacingVariant="condensed"
title="List Cell with Warning"
description="Shows usage with a warning message"
helperText={
<CellHelperText font="label2" variant="warning" paddingStart={6}>
This is a warning message.
</CellHelperText>
}
media={<Avatar src={assets.btc.imageUrl} />}
end={<Button compact>Action</Button>}
/>
<ListCell
spacingVariant="condensed"
title="List Cell with Error"
description="Shows usage with an error message"
helperText={
<CellHelperText font="label2" variant="error" paddingStart={6}>
This is an error message.
</CellHelperText>
}
media={<Avatar src={assets.btc.imageUrl} />}
end={<Button compact>Action</Button>}
/>
</VStack>
Loading States
The ListCellFallback component provides loading state representations of ListCell. It uses placeholder rectangles to indicate where content will appear, creating a smooth loading experience. The web version uses percentage-based widths and custom layouts to match the ListCell's four-column structure.
<VStack gap={3}>
{}
<ListCellFallback title description spacingVariant="condensed" />
{}
<ListCellFallback title description media="icon" spacingVariant="condensed" />
{}
<ListCellFallback title description detail subdetail spacingVariant="condensed" />
{}
<ListCellFallback
spacingVariant="condensed"
title
description
detail
subdetail
helperText
media="icon"
rectWidthVariant={2}
disableRandomRectWidth
styles={{ helperText: { paddingLeft: 48 } }}
/>
</VStack>
Priority
The priority prop controls which parts of the cell are protected from shrinking and truncation when horizontal space is limited. It accepts start, middle, and end as a string or an array of strings.
function PriorityContent() {
const dimensions = { width: 62, height: 18 };
const sparklineData = prices
.map((price) => parseFloat(price))
.filter((price, index) => index % 10 === 0);
const referenceY = sparklineData[Math.floor(sparklineData.length / 3)];
const CompactChart = memo(
({ data, color = 'var(--color-fgPositive)', showArea = false, referenceY }) => (
<Box style={{ padding: 1 }}>
<LineChart
{...dimensions}
enableScrubbing={false}
overflow="visible"
inset={0}
showArea={showArea}
series={[
{
id: 'series',
data,
color,
},
]}
>
<ReferenceLine dataY={referenceY} />
</LineChart>
</Box>
),
);
return (
<VStack gap={3} style={{ width: '100%', maxWidth: 320, overflow: 'hidden' }}>
<ListCell
spacingVariant="condensed"
title="Asset with a really long name"
description="Some description of the asset"
intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
detail="$334,239.03"
subdetail="+4.06%"
priority="start"
variant="positive"
/>
<ListCell
spacingVariant="condensed"
title="Asset with a really long name"
description="Some description of the asset"
intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
detail="$334,239.03"
subdetail="+4.06%"
priority="middle"
variant="positive"
/>
<ListCell
spacingVariant="condensed"
title="Asset with a really long name"
description="Some description of the asset"
intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
detail="$334,239.03"
subdetail="+4.06%"
priority="end"
variant="positive"
/>
<ListCell
spacingVariant="condensed"
title="Asset with a really long name"
description="Some description of the asset"
intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
detail="$334,239.03"
subdetail="+4.06%"
priority={['start', 'middle', 'end']}
variant="warning"
/>
</VStack>
);
}
Anatomy
Without helper text (top-only layout):
┌───────────────────────────────────────────────────────────────────────────┐
│ root (Box) │
│┌─────────────────────────────────────────────────────────────────────────┐│
││ pressable ││
││┌──────────────────── ───────────────────────────────────────────────────┐││
│││ contentContainer & mainContent (HStack) │││
│││ ┌─────┐ ┌────────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────┐ │││
│││ │media│ │ VStack │ │intermediary│ │ end │ │accessory│ │││
│││ │ │ │ ┌──────────┐ │ │ │ │ (detail │ │ │ │││
│││ │ │ │ │ title │ │ │ │ │ or │ │ │ │││
│││ │ │ │ └──────────┘ │ │ │ │ action) │ │ │ │││
│││ │ │ │ ┌────────────┐ │ │ │ │ │ │ │ │││
│││ │ │ │ │ description│ │ │ │ │ │ │ │ │││
│││ │ │ │ └────────────┘ │ │ │ │ │ │ │ │││
│││ └─────┘ └────────────────┘ └────────────┘ └────────────┘ └─────────┘ │││
││└───────────────────────────────────────────────────────────────────────┘││
│└─────────────────────────────────────────────────────────────────────────┘│
└───────────────────────────────────────────────────────────────────────────┘
With helper text (top + bottom layout):
┌─────────────────────────────────────────────────────────────────────────────┐
│ root (Box) │
│┌───────────────────────────────────────────────────────────────────────────┐│
││ pressable ││
││┌─────────────────────────────────────────────────────────────────────────┐││
│││ contentContainer (VStack) │││
│││┌───────────────────────────────────────────────────────────────────────┐│││
││││ mainContent (HStack) ││││
││││ ┌─────┐ ┌────────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────┐ ││││
││││ │media│ │ VStack │ │intermediary│ │ end │ │accessory│ ││││
││││ │ │ │ ┌──────────┐ │ │ │ │ (detail │ │ │ ││││
││││ │ │ │ │ title │ │ │ │ │ or │ │ │ ││││
││││ │ │ │ └──────────┘ │ │ │ │ action) │ │ │ ││││
││││ │ │ │ ┌────────────┐ │ │ │ │ │ │ │ ││││
││││ │ │ │ │ description│ │ │ │ │ │ │ │ ││││
││││ │ │ │ └────────────┘ │ │ │ │ │ │ │ ││││
││││ └─────┘ └────────────────┘ └────────────┘ └────────────┘ └─────────┘ ││││
│││└───────────────────────────────────────────────────────────────────────┘│││
│││┌───────────────────────────────────────────────────────────────────────┐│││
││││ helperText ││││
│││└───────────────────────────────────────────────────────────────────────┘│││
││└─────────────────────────────────────────────────────────────────────────┘││
│└──────────────────────────────────────────────────────────── ───────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘
Mapping to styles / classNames keys:
- root: outer
Box wrapping the entire cell
- pressable: interactive overlay when
href / onClick keyboard handlers are present
- contentContainer: container around top and optional bottom content
- mainContent: inner horizontal layout that holds the main pieces
- title/description: stacked text column within
topContent
- media: leading media container
- intermediary: middle container between main and end
- end: container for
detail / subdetail or end prop you pass in
- accessory: trailing accessory container
- helperText: container below main content to display helper text