alpha

ListView

Lists display a linear collection of data. They allow users to quickly scan, sort, compare, and take action on large amounts of data.

installyarn add @react-spectrum/list
version3.0.0-alpha.5
usageimport {Item, ListView} from '@react-spectrum/list'

Example#


<ListView aria-label="List example">
  <Item textValue="Adobe Photoshop">Adobe Photoshop</Item>
  <Item textValue="Adobe InDesign">Adobe InDesign</Item>
  <Item textValue="Adobe AfterEffects">Adobe AfterEffects</Item>
  <Item textValue="Adobe Illustrator">Adobe Illustrator</Item>
  <Item textValue="Adobe Lightroom">Adobe Lightroom</Item>
</ListView>
<ListView aria-label="List example">
  <Item textValue="Adobe Photoshop">Adobe Photoshop</Item>
  <Item textValue="Adobe InDesign">Adobe InDesign</Item>
  <Item textValue="Adobe AfterEffects">
    Adobe AfterEffects
  </Item>
  <Item textValue="Adobe Illustrator">
    Adobe Illustrator
  </Item>
  <Item textValue="Adobe Lightroom">Adobe Lightroom</Item>
</ListView>
<ListView aria-label="List example">
  <Item textValue="Adobe Photoshop">
    Adobe Photoshop
  </Item>
  <Item textValue="Adobe InDesign">
    Adobe InDesign
  </Item>
  <Item textValue="Adobe AfterEffects">
    Adobe AfterEffects
  </Item>
  <Item textValue="Adobe Illustrator">
    Adobe Illustrator
  </Item>
  <Item textValue="Adobe Lightroom">
    Adobe Lightroom
  </Item>
</ListView>

Content#


ListView is a collection component that provides users with a way to sort and organize lists.

Basic usage of ListView, seen in the example above, shows the use of a static collection where the contents of the ListView is hard coded. Dynamic collections, as shown below, can be used when the options come from an external data source such as an API, or update over time. Providing the data in this way allows ListView to automatically cache the rendering of each item, which dramatically improves performance.

Each has a unique key defined by the data. In the example below, the key of each row element is implicitly defined by the id property of the row object. See collections to learn more keys in dynamic collections.

const list = [
  {id: 1, name: 'Adobe Photoshop'},
  {id: 2, name: 'Adobe XD'},
  {id: 3, name: 'Adobe InDesign'},
  {id: 4, name: 'Adobe AfterEffects'},
  {id: 5, name: 'Adobe Illustrator'},
  {id: 6, name: 'Adobe Lightroom'},
  {id: 7, name: 'Adobe Premiere Pro'},
  {id: 8, name: 'Adobe Fresco'},
  {id: 9, name: 'Adobe Dreamweaver'}
];

<ListView items={list} height="250px" aria-label="List example">
  {(item) => (
    <Item key={item.key} textValue={item.name}>
      {item.name}
    </Item>
  )}
</ListView>
const list = [
  { id: 1, name: 'Adobe Photoshop' },
  { id: 2, name: 'Adobe XD' },
  { id: 3, name: 'Adobe InDesign' },
  { id: 4, name: 'Adobe AfterEffects' },
  { id: 5, name: 'Adobe Illustrator' },
  { id: 6, name: 'Adobe Lightroom' },
  { id: 7, name: 'Adobe Premiere Pro' },
  { id: 8, name: 'Adobe Fresco' },
  { id: 9, name: 'Adobe Dreamweaver' }
];

<ListView
  items={list}
  height="250px"
  aria-label="List example"
>
  {(item) => (
    <Item key={item.key} textValue={item.name}>
      {item.name}
    </Item>
  )}
</ListView>
const list = [
  {
    id: 1,
    name:
      'Adobe Photoshop'
  },
  {
    id: 2,
    name: 'Adobe XD'
  },
  {
    id: 3,
    name:
      'Adobe InDesign'
  },
  {
    id: 4,
    name:
      'Adobe AfterEffects'
  },
  {
    id: 5,
    name:
      'Adobe Illustrator'
  },
  {
    id: 6,
    name:
      'Adobe Lightroom'
  },
  {
    id: 7,
    name:
      'Adobe Premiere Pro'
  },
  {
    id: 8,
    name: 'Adobe Fresco'
  },
  {
    id: 9,
    name:
      'Adobe Dreamweaver'
  }
];

<ListView
  items={list}
  height="250px"
  aria-label="List example"
>
  {(item) => (
    <Item
      key={item.key}
      textValue={item
        .name}
    >
      {item.name}
    </Item>
  )}
</ListView>

Internationalization#

To internationalize a ListView, all text content within the ListView should be localized. This includes the aria-label provided to the ListView if any. For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of ListView is automatically flipped.

Labeling#


Accessibility#

An aria-label must be provided to the ListView for accessibility. If the ListView is labeled by a separate element, an aria-labelledby prop must be provided using the id of the labeling element instead.

Asynchronous loading#


ListView supports loading data asynchronously, and will display a progress circle reflecting the current load state, set by the loadingState prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the onLoadMore prop.

This example uses the useAsyncList hook to handle loading the data. See the docs for more information.

import {useAsyncList} from '@react-stately/data';

function AsyncList() {
  let list = useAsyncList({
    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="example async loading list"
      height="size-3000"
      items={list.items}
      loadingState={list.loadingState}
      onLoadMore={list.loadMore}
    >
      {(item) => <Item key={item.name} textValue={item.name}>{item.name}</Item>}
    </ListView>
  );
}
import {useAsyncList} from '@react-stately/data';

function AsyncList() {
  let list = useAsyncList({
    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="example async loading list"
      height="size-3000"
      items={list.items}
      loadingState={list.loadingState}
      onLoadMore={list.loadMore}
    >
      {(item) => (
        <Item key={item.name} textValue={item.name}>
          {item.name}
        </Item>
      )}
    </ListView>
  );
}
import {useAsyncList} from '@react-stately/data';

function AsyncList() {
  let list =
    useAsyncList({
      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="example async loading list"
      height="size-3000"
      items={list.items}
      loadingState={list
        .loadingState}
      onLoadMore={list
        .loadMore}
    >
      {(item) => (
        <Item
          key={item.name}
          textValue={item
            .name}
        >
          {item.name}
        </Item>
      )}
    </ListView>
  );
}

Complex items#


Items within a ListView also allow for additional content used to add context or provide additional actions to Items. Icons, links, and descriptions can be added to the children of Item as shown in the example below. If a description is added, the prop slot="description" must be used to distinguish the different <Text> elements. Additionally, components such as <ActionButton>, <ActionGroup>, and <ActionMenu> will be styled appropriately if included within an item.

<ListView aria-label="List example with complex items">
  <Item textValue="Row with link, icon, and description">
    <Folder />
    <Link>Row with link, icon, and description</Link>
    <Text slot="description">Description of row</Text>
  </Item>
  <Item textValue="Row with action button">
    Row with action button
    <ActionButton aria-label="Info">
      <Info />
    </ActionButton>
  </Item>
  <Item textValue="Row with action group">
    Row with action group
    <ActionGroup buttonLabelBehavior="hide">
      <Item key="edit">
        <Edit />
        <Text>Edit</Text>
      </Item>
      <Item key="delete">
        <Delete />
        <Text>Delete</Text>
      </Item>
    </ActionGroup>
  </Item>
  <Item textValue="Row with action menu">
    Row with action menu
    <ActionMenu>
      <Item key="edit" textValue="Edit">
        <Edit />
        <Text>Edit</Text>
      </Item>
      <Item key="delete" textValue="Delete">
        <Delete />
        <Text>Delete</Text>
      </Item>
    </ActionMenu>
  </Item>
</ListView>
<ListView aria-label="List example with complex items">
  <Item textValue="Row with link, icon, and description">
    <Folder />
    <Link>Row with link, icon, and description</Link>
    <Text slot="description">Description of row</Text>
  </Item>
  <Item textValue="Row with action button">
    Row with action button
    <ActionButton aria-label="Info">
      <Info />
    </ActionButton>
  </Item>
  <Item textValue="Row with action group">
    Row with action group
    <ActionGroup buttonLabelBehavior="hide">
      <Item key="edit">
        <Edit />
        <Text>Edit</Text>
      </Item>
      <Item key="delete">
        <Delete />
        <Text>Delete</Text>
      </Item>
    </ActionGroup>
  </Item>
  <Item textValue="Row with action menu">
    Row with action menu
    <ActionMenu>
      <Item key="edit" textValue="Edit">
        <Edit />
        <Text>Edit</Text>
      </Item>
      <Item key="delete" textValue="Delete">
        <Delete />
        <Text>Delete</Text>
      </Item>
    </ActionMenu>
  </Item>
</ListView>
<ListView aria-label="List example with complex items">
  <Item textValue="Row with link, icon, and description">
    <Folder />
    <Link>
      Row with link,
      icon, and
      description
    </Link>
    <Text slot="description">
      Description of
      row
    </Text>
  </Item>
  <Item textValue="Row with action button">
    Row with action
    button
    <ActionButton aria-label="Info">
      <Info />
    </ActionButton>
  </Item>
  <Item textValue="Row with action group">
    Row with action
    group
    <ActionGroup buttonLabelBehavior="hide">
      <Item key="edit">
        <Edit />
        <Text>
          Edit
        </Text>
      </Item>
      <Item key="delete">
        <Delete />
        <Text>
          Delete
        </Text>
      </Item>
    </ActionGroup>
  </Item>
  <Item textValue="Row with action menu">
    Row with action
    menu
    <ActionMenu>
      <Item
        key="edit"
        textValue="Edit"
      >
        <Edit />
        <Text>
          Edit
        </Text>
      </Item>
      <Item
        key="delete"
        textValue="Delete"
      >
        <Delete />
        <Text>
          Delete
        </Text>
      </Item>
    </ActionMenu>
  </Item>
</ListView>

Selection#


By default, ListView doesn't allow row selection but this can be enabled using the selectionMode prop. Use defaultSelectedKeys to provide a default set of selected rows. Note that the value of the selected keys must match the key prop of the Item.

The example below enables multiple selection mode, and uses defaultSelectedKeys to select the rows with keys "row1" and "row3".

<ListView
  selectionMode="multiple"
  defaultSelectedKeys={['row1', 'row3']}
  aria-label="List seletion example"
>
  <Item key="row1" textValue="row 1">
    <Content>row 1</Content>
  </Item>
  <Item key="row2" textValue="row 2">
    <Content>row 2</Content>
  </Item>
  <Item key="row3" textValue="row 3">
    <Content>row 3</Content>
  </Item>
</ListView>
<ListView
  selectionMode="multiple"
  defaultSelectedKeys={['row1', 'row3']}
  aria-label="List seletion example"
>
  <Item key="row1" textValue="row 1">
    <Content>row 1</Content>
  </Item>
  <Item key="row2" textValue="row 2">
    <Content>row 2</Content>
  </Item>
  <Item key="row3" textValue="row 3">
    <Content>row 3</Content>
  </Item>
</ListView>
<ListView
  selectionMode="multiple"
  defaultSelectedKeys={[
    'row1',
    'row3'
  ]}
  aria-label="List seletion example"
>
  <Item
    key="row1"
    textValue="row 1"
  >
    <Content>
      row 1
    </Content>
  </Item>
  <Item
    key="row2"
    textValue="row 2"
  >
    <Content>
      row 2
    </Content>
  </Item>
  <Item
    key="row3"
    textValue="row 3"
  >
    <Content>
      row 3
    </Content>
  </Item>
</ListView>

Controlled selection#

To programmatically control row selection, use the selectedKeys prop paired with the onSelectionChange callback. The key prop from the selected rows will be passed into the callback when the row is pressed, allowing you to update state accordingly.

Here is how you would control selection for the above example.

function PokemonList(props) {
  let rows = [
    { id: 1, name: 'Charizard' },
    { id: 2, name: 'Blastoise' },
    { id: 3, name: 'Venusaur' },
    { id: 4, name: 'Pikachu' }
  ];

  let [selectedKeys, setSelectedKeys] = React.useState(new Set([2]));

  return (
    <ListView
      items={rows}
      aria-label="List with controlled selection"
      selectionMode="multiple"
      selectedKeys={selectedKeys}
      onSelectionChange={setSelectedKeys}
      {...props}
    >
      {(item) => (
        <Item key={item.id} textValue={item.name}>
          {item.name}
        </Item>
      )}
    </ListView>
  );
}
function PokemonList(props) {
  let rows = [
    { id: 1, name: 'Charizard' },
    { id: 2, name: 'Blastoise' },
    { id: 3, name: 'Venusaur' },
    { id: 4, name: 'Pikachu' }
  ];

  let [selectedKeys, setSelectedKeys] = React.useState(
    new Set([2])
  );

  return (
    <ListView
      items={rows}
      aria-label="List with controlled selection"
      selectionMode="multiple"
      selectedKeys={selectedKeys}
      onSelectionChange={setSelectedKeys}
      {...props}
    >
      {(item) => (
        <Item key={item.id} textValue={item.name}>
          {item.name}
        </Item>
      )}
    </ListView>
  );
}
function PokemonList(
  props
) {
  let rows = [
    {
      id: 1,
      name: 'Charizard'
    },
    {
      id: 2,
      name: 'Blastoise'
    },
    {
      id: 3,
      name: 'Venusaur'
    },
    {
      id: 4,
      name: 'Pikachu'
    }
  ];

  let [
    selectedKeys,
    setSelectedKeys
  ] = React.useState(
    new Set([2])
  );

  return (
    <ListView
      items={rows}
      aria-label="List with controlled selection"
      selectionMode="multiple"
      selectedKeys={selectedKeys}
      onSelectionChange={setSelectedKeys}
      {...props}
    >
      {(item) => (
        <Item
          key={item.id}
          textValue={item
            .name}
        >
          {item.name}
        </Item>
      )}
    </ListView>
  );
}

Single selection#

To limit users to selecting only a single item at a time, selectionMode can be set to single.

// Using the same list as above
<PokemonList selectionMode="single" />
// Using the same list as above
<PokemonList selectionMode="single" />
// Using the same list as above
<PokemonList selectionMode="single" />

Disallow empty selection#

ListView also supports a disallowEmptySelection prop which forces the user to have at least one row in the ListView selected at all times. In this mode, if a single row is selected and the user presses it, it will not be deselected.

// Using the same list as above
<PokemonList disallowEmptySelection />
// Using the same list as above
<PokemonList disallowEmptySelection />
// Using the same list as above
<PokemonList
  disallowEmptySelection
/>

Disabled rows#

You can disable specific rows by providing an array of keys to ListView via the disabledKeys prop. This will prevent rows from being selectable as shown in the example below.

// Using the same list as above
<PokemonList selectionMode="multiple" disabledKeys={[3]} />
// Using the same list as above
<PokemonList selectionMode="multiple" disabledKeys={[3]} />
// Using the same list as above
<PokemonList
  selectionMode="multiple"
  disabledKeys={[3]}
/>

Highlight selection#

By default, ListView uses the checkbox selection style, which includes a checkbox in each row for selection. When the selectionStyle prop is set to "highlight", the checkboxes are hidden, and the selected rows are displayed with a highlighted background instead.

In addition to changing the appearance, the selection behavior also changes depending on the selectionStyle prop. In the default checkbox selection style, clicking, tapping, or pressing the Space or Enter keys toggles selection for the focused row. Using the arrow keys moves focus but does not change selection.

In the highlight selection style, however, clicking a row with the mouse replaces the selection with only that row. Using the arrow keys moves both focus and selection. To select multiple rows, modifier keys such as Ctrl, Cmd, and Shift can be used. To move focus without moving selection, the Ctrl key on Windows or the Option key on macOS can be held while pressing the arrow keys. Holding this modifier while pressing the Space key toggles selection for the focused row, which allows multiple selection of non-contiguous items. On touch screen devices, selection always behaves as toggle since modifier keys may not be available. This behavior emulates native platforms such as macOS and Windows.

<PokemonList selectionMode="multiple" selectionStyle="highlight" />
<PokemonList
  selectionMode="multiple"
  selectionStyle="highlight"
/>
<PokemonList
  selectionMode="multiple"
  selectionStyle="highlight"
/>

Row actions#

ListView may be used in use cases where users can perform actions on rows, such as navigating into items to open them or get more details. In the default checkbox selection style, it is recommended to use a Link component for navigation.

In the highlight selection style, the onAction prop can be used to enable row actions, which differ depending on the interaction method. When provided, double clicking with a mouse or pressing the Enter key triggers onAction, while single click and the Space key are reserved for selection. On touch devices, onAction becomes the primary tap interaction, and a long press enters into selection mode, which displays checkboxes to perform selection. Deselecting all items exits selection mode and hides the checkboxes. Double clicking matches the behavior of desktop platforms like macOS and Windows, and a separate selection mode matches mobile platforms like iOS and Android.

<PokemonList
  selectionMode="multiple"
  selectionStyle="highlight"
  onAction={(key) => alert(`Opening item ${key}...`)}
/>
<PokemonList
  selectionMode="multiple"
  selectionStyle="highlight"
  onAction={(key) => alert(`Opening item ${key}...`)}
/>
<PokemonList
  selectionMode="multiple"
  selectionStyle="highlight"
  onAction={(key) =>
    alert(
      `Opening item ${key}...`
    )}
/>

Props#


NameTypeDefaultDescription
childrenCollectionChildren<T>The contents of the collection.
density'compact''regular''spacious''regular'Sets the amount of vertical padding within each cell.
isQuietboolean
loadingStateLoadingState
renderEmptyState() => JSX.Element
transitionDurationnumber
itemsIterable<T>Item objects in the collection.
disabledKeysIterable<Key>The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.
selectionModeSelectionModeThe type of selection that is allowed in the collection.
disallowEmptySelectionbooleanWhether the collection allows empty selection.
selectedKeys'all'Iterable<Key>The currently selected keys in the collection (controlled).
defaultSelectedKeys'all'Iterable<Key>The initial selected keys in the collection (uncontrolled).
selectionStyle'checkbox''highlight'How selection should be displayed.
Events
NameTypeDefaultDescription
onAction( (key: string )) => void
onSelectionChange( (keys: Selection )) => anyHandler that is called when the selection changes.
Layout
NameTypeDefaultDescription
flexResponsive<stringnumberboolean>When used in a flex layout, specifies how the element will grow or shrink to fit the space available. See MDN.
flexGrowResponsive<number>When used in a flex layout, specifies how the element will grow to fit the space available. See MDN.
flexShrinkResponsive<number>When used in a flex layout, specifies how the element will shrink to fit the space available. See MDN.
flexBasisResponsive<numberstring>When used in a flex layout, specifies the initial main size of the element. See MDN.
alignSelfResponsive<'auto''normal''start''end''center''flex-start''flex-end''self-start''self-end''stretch'>Overrides the alignItems property of a flex or grid container. See MDN.
justifySelfResponsive<'auto''normal''start''end''flex-start''flex-end''self-start''self-end''center''left''right''stretch'>Specifies how the element is justified inside a flex or grid container. See MDN.
orderResponsive<number>The layout order for the element within a flex or grid container. See MDN.
gridAreaResponsive<string>When used in a grid layout, specifies the named grid area that the element should be placed in within the grid. See MDN.
gridColumnResponsive<string>When used in a grid layout, specifies the column the element should be placed in within the grid. See MDN.
gridRowResponsive<string>When used in a grid layout, specifies the row the element should be placed in within the grid. See MDN.
gridColumnStartResponsive<string>When used in a grid layout, specifies the starting column to span within the grid. See MDN.
gridColumnEndResponsive<string>When used in a grid layout, specifies the ending column to span within the grid. See MDN.
gridRowStartResponsive<string>When used in a grid layout, specifies the starting row to span within the grid. See MDN.
gridRowEndResponsive<string>When used in a grid layout, specifies the ending row to span within the grid. See MDN.
Spacing
NameTypeDefaultDescription
marginResponsive<DimensionValue>The margin for all four sides of the element. See MDN.
marginTopResponsive<DimensionValue>The margin for the top side of the element. See MDN.
marginBottomResponsive<DimensionValue>The margin for the bottom side of the element. See MDN.
marginStartResponsive<DimensionValue>The margin for the logical start side of the element, depending on layout direction. See MDN.
marginEndResponsive<DimensionValue>The margin for the logical end side of an element, depending on layout direction. See MDN.
marginXResponsive<DimensionValue>The margin for both the left and right sides of the element. See MDN.
marginYResponsive<DimensionValue>The margin for both the top and bottom sides of the element. See MDN.
Sizing
NameTypeDefaultDescription
widthResponsive<DimensionValue>The width of the element. See MDN.
minWidthResponsive<DimensionValue>The minimum width of the element. See MDN.
maxWidthResponsive<DimensionValue>The maximum width of the element. See MDN.
heightResponsive<DimensionValue>The height of the element. See MDN.
minHeightResponsive<DimensionValue>The minimum height of the element. See MDN.
maxHeightResponsive<DimensionValue>The maximum height of the element. See MDN.
Positioning
NameTypeDefaultDescription
positionResponsive<'static''relative''absolute''fixed''sticky'>Specifies how the element is positioned. See MDN.
topResponsive<DimensionValue>The top position for the element. See MDN.
bottomResponsive<DimensionValue>The bottom position for the element. See MDN.
leftResponsive<DimensionValue>The left position for the element. See MDN. Consider using start instead for RTL support.
rightResponsive<DimensionValue>The right position for the element. See MDN. Consider using start instead for RTL support.
startResponsive<DimensionValue>The logical start position for the element, depending on layout direction. See MDN.
endResponsive<DimensionValue>The logical end position for the element, depending on layout direction. See MDN.
zIndexResponsive<number>The stacking order for the element. See MDN.
isHiddenResponsive<boolean>Hides the element.
Accessibility
NameTypeDefaultDescription
idstringThe element's unique identifier. See MDN.
aria-labelstringDefines a string value that labels the current element.
aria-labelledbystringIdentifies the element (or elements) that labels the current element.
aria-describedbystringIdentifies the element (or elements) that describes the object.
aria-detailsstringIdentifies the element (or elements) that provide a detailed, extended description for the object.
Advanced
NameTypeDefaultDescription
UNSAFE_classNamestringSets the CSS className for the element. Only use as a last resort. Use style props instead.
UNSAFE_styleCSSPropertiesSets inline style for the element. Only use as a last resort. Use style props instead.

Visual options#


Quiet#

<ListView isQuiet aria-label="Quiet list example">
  <Item textValue="row 1">row 1</Item>
  <Item textValue="row 2">row 2</Item>
  <Item textValue="row 3">row 3</Item>
</ListView>
<ListView isQuiet aria-label="Quiet list example">
  <Item textValue="row 1">row 1</Item>
  <Item textValue="row 2">row 2</Item>
  <Item textValue="row 3">row 3</Item>
</ListView>
<ListView
  isQuiet
  aria-label="Quiet list example"
>
  <Item textValue="row 1">
    row 1
  </Item>
  <Item textValue="row 2">
    row 2
  </Item>
  <Item textValue="row 3">
    row 3
  </Item>
</ListView>

Density#

The amount of vertical padding that each row contains can be modified by providing the density prop.

<Flex direction="column" gap="size-300">
    <Text>Compact:</Text>
    <ListView density="compact" aria-label="Compact list example">
      <Item textValue="row 1">row 1</Item>
      <Item textValue="row 2">row 2</Item>
      <Item textValue="row 3">row 3</Item>
    </ListView>
    <Text>Spacious:</Text>
    <ListView density="spacious" aria-label="Spacious list example">
      <Item textValue="row 1">row 1</Item>
      <Item textValue="row 2">row 2</Item>
      <Item textValue="row 3">row 3</Item>
    </ListView>
</Flex>
<Flex direction="column" gap="size-300">
  <Text>Compact:</Text>
  <ListView
    density="compact"
    aria-label="Compact list example"
  >
    <Item textValue="row 1">row 1</Item>
    <Item textValue="row 2">row 2</Item>
    <Item textValue="row 3">row 3</Item>
  </ListView>
  <Text>Spacious:</Text>
  <ListView
    density="spacious"
    aria-label="Spacious list example"
  >
    <Item textValue="row 1">row 1</Item>
    <Item textValue="row 2">row 2</Item>
    <Item textValue="row 3">row 3</Item>
  </ListView>
</Flex>
<Flex
  direction="column"
  gap="size-300"
>
  <Text>Compact:</Text>
  <ListView
    density="compact"
    aria-label="Compact list example"
  >
    <Item textValue="row 1">
      row 1
    </Item>
    <Item textValue="row 2">
      row 2
    </Item>
    <Item textValue="row 3">
      row 3
    </Item>
  </ListView>
  <Text>
    Spacious:
  </Text>
  <ListView
    density="spacious"
    aria-label="Spacious list example"
  >
    <Item textValue="row 1">
      row 1
    </Item>
    <Item textValue="row 2">
      row 2
    </Item>
    <Item textValue="row 3">
      row 3
    </Item>
  </ListView>
</Flex>

Empty state#

Use the renderEmptyState prop to customize what the ListView will display if there are no rows provided.

import {Content} from '@react-spectrum/view';
import {IllustratedMessage} from '@react-spectrum/illustratedmessage';
import NotFound from '@spectrum-icons/illustrations/NotFound';
import {Heading} from '@react-spectrum/text';

function renderEmptyState() {
  return (
    <IllustratedMessage>
      <NotFound />
      <Heading>No results</Heading>
      <Content>No results found</Content>
    </IllustratedMessage>
  );
}

<ListView
  aria-label="Example list for empty state"
  height="size-3000" 
  renderEmptyState={renderEmptyState}>
  {[]}
</ListView>
import {Content} from '@react-spectrum/view';
import {IllustratedMessage} from '@react-spectrum/illustratedmessage';
import NotFound from '@spectrum-icons/illustrations/NotFound';
import {Heading} from '@react-spectrum/text';

function renderEmptyState() {
  return (
    <IllustratedMessage>
      <NotFound />
      <Heading>No results</Heading>
      <Content>No results found</Content>
    </IllustratedMessage>
  );
}

<ListView
  aria-label="Example list for empty state"
  height="size-3000"
  renderEmptyState={renderEmptyState}
>
  {[]}
</ListView>
import {Content} from '@react-spectrum/view';
import {IllustratedMessage} from '@react-spectrum/illustratedmessage';
import NotFound from '@spectrum-icons/illustrations/NotFound';
import {Heading} from '@react-spectrum/text';

function renderEmptyState() {
  return (
    <IllustratedMessage>
      <NotFound />
      <Heading>
        No results
      </Heading>
      <Content>
        No results found
      </Content>
    </IllustratedMessage>
  );
}

<ListView
  aria-label="Example list for empty state"
  height="size-3000"
  renderEmptyState={renderEmptyState}
>
  {[]}
</ListView>