A ListView displays a list of interactive items, and allows a user to navigate, select, or perform an action.
Content
ListView follows the Collection Components API, accepting both static and dynamic collections. This example shows a dynamic collection, passing a list of objects to the items prop, and a function to render the children.
import {ListView, ListViewItem} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
<ListView aria-label="Dynamic files" items={files} styles={style({width: 'full', maxWidth: 400, height: 320})}>
{item => (
<ListViewItem id={item.id}>{item.name}</ListViewItem>
)}
</ListView>
Slots
ListViewItem supports icons, Text, Image, ActionMenu, and ActionButtonGroup as children, and must have a textValue prop for accessibility.
import {ActionButton, ActionButtonGroup, ActionMenu, Image, ListView, ListViewItem, MenuItem, Text} from '@react-spectrum/s2';
import Edit from '@react-spectrum/s2/icons/Edit';
import Copy from '@react-spectrum/s2/icons/Copy';
import Delete from '@react-spectrum/s2/icons/Delete';
import File from '@react-spectrum/s2/icons/File';
import Share from '@react-spectrum/s2/icons/Share';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
<div className={style({display: 'flex', gap: 24, width: 'full', flexWrap: 'wrap', justifyContent: 'center'})}>
<ListView aria-label="Documents" items={documents} styles={style({flexGrow: 1, flexShrink: 1, flexBasis: 0, minWidth: 200, maxWidth: 400, height: 320})}>
{item => (
<ListViewItem id={item.id} textValue={item.name}>
<File />
<Text>{item.name}</Text>
<Text slot="description">Document</Text>
<ActionButtonGroup aria-label="File actions">
<ActionButton aria-label="Edit">
<Edit />
</ActionButton>
<ActionButton aria-label="Delete">
<Delete />
</ActionButton>
</ActionButtonGroup>
</ListViewItem>
)}
</ListView>
<ListView aria-label="Images" items={images} styles={style({flexGrow: 1, flexShrink: 1, flexBasis: 0, minWidth: 200, maxWidth: 400, height: 320})}>
{item => (
<ListViewItem id={item.id} textValue={item.name}>
<Image src={item.image_url} alt={item.name} />
<Text>{item.name}</Text>
<Text slot="description">Image</Text>
<ActionMenu>
<MenuItem>
<Copy />
<Text>Copy</Text>
</MenuItem>
<MenuItem>
<Share />
<Text>Share</Text>
</MenuItem>
<MenuItem>
<Delete />
<Text>Delete</Text>
</MenuItem>
</ActionMenu>
</ListViewItem>
)}
</ListView>
</div>
Asynchronous loading
Use the loadingState and onLoadMore props to enable async loading and infinite scrolling.
import {ListView, ListViewItem} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {useAsyncList} from 'react-stately';
interface Character {
name: string
}
function AsyncListView() {
let list = useAsyncList<Character>({
async load({signal, cursor}) {
if (cursor) {
cursor = cursor.replace(/^http:\/\//i, 'https://');
}
let res = await fetch(cursor || 'https://swapi.py4e.com/api/people/?search=', {signal});
let json = await res.json();
return {
items: json.results,
cursor: json.next
};
}
});
return (
<ListView
aria-label="Star Wars characters"
loadingState={list.loadingState}
onLoadMore={list.loadMore}
items={list.items}
styles={style({width: 'full', maxWidth: 400, height: 320})}>
{item => (
<ListViewItem id={item.name}>{item.name}</ListViewItem>
)}
</ListView>
);
}
Links
Use the href prop on a ListViewItem to create a link. See the getting started guide to learn how to integrate with your framework. Link interactions vary depending on the selection behavior. See the selection guide for more details.
Empty state
Use renderEmptyState to render placeholder content when there are no items.
No results
import {ListView, IllustratedMessage, Heading, Content, Link} from '@react-spectrum/s2';
import FolderOpen from '@react-spectrum/s2/illustrations/linear/FolderOpen';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
<ListView
aria-label="Search results"
styles={style({width: 'full', maxWidth: 400})}
renderEmptyState={() => (
<IllustratedMessage>
<FolderOpen />
<Heading>No results</Heading>
<Content>Press <Link href="https://adobe.com">here</Link> for more info.</Content>
</IllustratedMessage>
)}>
{[]}
</ListView>
Selection and actions
Use the selectionMode prop to enable single or multiple selection. The selected items can be controlled via the selectedKeys prop, matching the id prop of the items. The onAction event handles item actions. Items can be disabled with the isDisabled prop. See the selection guide for more details.
Current selection:
Navigation
Use the hasChildItems prop on ListViewItem to display a chevron indicator, signaling that an item can be navigated into. Combine with onAction and Breadcrumbs to build a drill-down navigation pattern.
- Root
import {Breadcrumbs, Breadcrumb, ListView, ListViewItem, Text} from '@react-spectrum/s2';
import App from '@react-spectrum/s2/icons/App';
import Folder from '@react-spectrum/s2/icons/Folder';
import File from '@react-spectrum/s2/icons/File';
import Image from '@react-spectrum/s2/icons/Image';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {Key} from 'react-aria';
import {useState} from 'react';
function NavigationExample() {
let [breadcrumbs, setBreadcrumbs] = useState<BreadcrumbItem[]>([
{id: 'root', name: 'Root', icon: <Folder />, children: rootItems}
]);
let currentItems = breadcrumbs[breadcrumbs.length - 1].children;
let onAction = (key: Key) => {
let item = currentItems.find(item => item.id === key);
if (item && 'children' in item) {
setBreadcrumbs(prev => [...prev, item]);
}
};
let onBreadcrumbAction = (key: Key) => {
let index = breadcrumbs.findIndex(item => item.id === key);
setBreadcrumbs(breadcrumbs.slice(0, index + 1));
};
return (
<div className={style({display: 'flex', flexDirection: 'column', gap: 8, width: 'full', maxWidth: 400})}>
<Breadcrumbs onAction={onBreadcrumbAction}>
{breadcrumbs.map(item => (
<Breadcrumb key={item.id} id={item.id}>{item.name}</Breadcrumb>
))}
</Breadcrumbs>
<ListView
aria-label={breadcrumbs[breadcrumbs.length - 1].name}
items={currentItems}
onAction={onAction}
styles={style({width: 'full', height: 320})}>
{item => (
<ListViewItem id={item.id} textValue={item.name} hasChildItems={item && 'children' in item}>
{item.icon}
<Text>{item.name}</Text>
</ListViewItem>
)}
</ListView>
</div>
);
}
API
<ListView>
<ListViewItem>
<Icon />
<Image />
<Text />
<Text slot="description" />
<ActionMenu /> or <ActionButtonGroup />
</ListViewItem>
</ListView>
ListView
| Name | Type | Default |
|---|---|---|
styles | StylesPropWithHeight | Default: — |
Spectrum-defined styles, returned by the | ||
renderActionBar | | Default: — |
Provides the ActionBar to display when items are selected in the ListView. | ||
hideLinkOutIcon | boolean | Default: — |
Hides the default link out icons on items that open links in a new tab. | ||
disallowTypeAhead | boolean | Default: false
|
Whether typeahead navigation is disabled. | ||
isQuiet | boolean | Default: — |
Whether the ListView should be displayed with a quiet style. | ||
selectionStyle | 'highlight' | 'checkbox' | Default: 'checkbox'
|
How selection should be displayed. | ||
overflowMode | 'wrap' | 'truncate' | Default: 'truncate'
|
Sets the overflow behavior for item contents. | ||
children | ReactNode | | Default: — |
The children of the ListView. | ||
items | Iterable | Default: — |
Item objects in the collection. | ||
loadingState | LoadingState | Default: — |
The current loading state of the ListView. | ||
onLoadMore | | Default: — |
Handler that is called when more items should be loaded, e.g. while scrolling near the bottom. | ||
renderEmptyState | | Default: — |
Provides content to display when there are no items in the list. | ||
dependencies | ReadonlyArray | Default: — |
Values that should invalidate the item cache when using dynamic collections. | ||
selectionMode | SelectionMode | Default: — |
The type of selection that is allowed in the collection. | ||
selectedKeys | 'all' | Iterable | Default: — |
The currently selected keys in the collection (controlled). | ||
defaultSelectedKeys | 'all' | Iterable | Default: — |
The initial selected keys in the collection (uncontrolled). | ||
onSelectionChange | | Default: — |
Handler that is called when the selection changes. | ||
disabledKeys | Iterable | Default: — |
The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with. | ||
disabledBehavior | DisabledBehavior | Default: "all"
|
Whether | ||
disallowEmptySelection | boolean | Default: — |
Whether the collection allows empty selection. | ||
shouldSelectOnPressUp | boolean | Default: — |
Whether selection should occur on press up instead of press down. | ||
escapeKeyBehavior | 'clearSelection' | 'none' | Default: 'clearSelection'
|
Whether pressing the escape key should clear selection in the grid list or not. Most experiences should not modify this option as it eliminates a keyboard user's ability to easily clear selection. Only use if the escape key is being handled externally or should not trigger selection clearing contextually. | ||
ListViewItem
| Name | Type | |
|---|---|---|
children | ReactNode | |
The contents of the item. | ||
hasChildItems | boolean | |
Whether the item has child items (renders a chevron indicator). | ||
id | Key | |
The unique id of the item. | ||
value | T | |
The object value that this item represents. When using dynamic collections, this is set automatically. | ||
textValue | string | |
A string representation of the item's contents, used for features like typeahead. | ||
isDisabled | boolean | |
Whether the item is disabled. | ||