Expo UI and Router for iOS
Components

List

A SwiftUI List component for displaying scrollable lists of items.

Expo UI List matches the official SwiftUI List API and supports styling via the listStyle modifier, various row/section modifiers, as well as selection, reordering, and editing capabilities.

List does not lazily render rows yet: React creates every row up front, so large lists can be slow to mount. We are working on improving this. For large lists, we recommend FlashList or Legend List.

Usage

Basic list

import { Host, List, Text, Section } from '@expo/ui/swift-ui';

export default function BasicListExample() {
  return (
    <Host style={{ flex: 1 }}>
      <List>
        <Section title="Fruits">
          <Text>Apple</Text>
          <Text>Banana</Text>
          <Text>Orange</Text>
        </Section>
        <Section title="Vegetables">
          <Text>Carrot</Text>
          <Text>Broccoli</Text>
          <Text>Spinach</Text>
        </Section>
      </List>
    </Host>
  );
}

List with labels and icons

import { Host, List, Label, Section } from '@expo/ui/swift-ui';

export default function ListWithLabelsExample() {
  return (
    <Host style={{ flex: 1 }}>
      <List>
        <Section title="Settings">
          <Label title="Wi-Fi" systemImage="wifi" />
          <Label
            title="Bluetooth"
            systemImage="antenna.radiowaves.left.and.right"
          />
          <Label
            title="Cellular"
            systemImage="antenna.radiowaves.left.and.right.circle"
          />
        </Section>
      </List>
    </Host>
  );
}

List styles

Use the listStyle modifier to change the list's appearance.

Note: The inset, insetGrouped, and sidebar styles are not available on tvOS.

import { useState } from 'react';
import {
  Host,
  List,
  Text,
  Section,
  Picker,
} from '@expo/ui/swift-ui';
import {
  listStyle,
  pickerStyle,
  tag,
} from '@expo/ui/swift-ui/modifiers';

const styles = [
  'automatic',
  'plain',
  'inset',
  'insetGrouped',
  'grouped',
  'sidebar',
] as const;

export default function ListStylesExample() {
  const [styleIndex, setStyleIndex] = useState(0);

  return (
    <Host style={{ flex: 1 }}>
      <List modifiers={[listStyle(styles[styleIndex])]}>
        <Section title="Style Picker">
          <Picker
            label="List Style"
            selection={styleIndex}
            onSelectionChange={setStyleIndex}
            modifiers={[pickerStyle('menu')]}>
            {styles.map((style, index) => (
              <Text key={style} modifiers={[tag(index)]}>
                {style}
              </Text>
            ))}
          </Picker>
        </Section>
        <Section title="Sample Items">
          <Text>Item 1</Text>
          <Text>Item 2</Text>
          <Text>Item 3</Text>
        </Section>
      </List>
    </Host>
  );
}

Selection and edit mode

Enable selection, deletion, and reordering of list items using the List.ForEach compound component with onDelete and onMove props.

import { useState } from 'react';
import {
  Host,
  List,
  Label,
  Section,
  Button,
  Toggle,
} from '@expo/ui/swift-ui';
import { environment, tag } from '@expo/ui/swift-ui/modifiers';

type Task = { id: string; title: string };

const INITIAL_TASKS: Task[] = [
  { id: '1', title: 'Task 1' },
  { id: '2', title: 'Task 2' },
  { id: '3', title: 'Task 3' },
  { id: '4', title: 'Task 4' },
];

export default function EditableListExample() {
  const [tasks, setTasks] = useState<Task[]>(INITIAL_TASKS);
  const [selectedIds, setSelectedIds] = useState<string[]>([]);
  const [editMode, setEditMode] = useState(false);

  const handleDelete = (indices: number[]) => {
    setTasks(prev => prev.filter((_, i) => !indices.includes(i)));
  };

  const handleMove = (
    sourceIndices: number[],
    destination: number
  ) => {
    setTasks(prev => {
      const newTasks = [...prev];
      const [removed] = newTasks.splice(sourceIndices[0], 1);
      const adjustedDest =
        sourceIndices[0] < destination
          ? destination - 1
          : destination;
      newTasks.splice(adjustedDest, 0, removed);
      return newTasks;
    });
  };

  return (
    <Host style={{ flex: 1 }}>
      <List
        selection={selectedIds}
        onSelectionChange={ids => setSelectedIds(ids.map(String))}
        modifiers={[
          environment('editMode', editMode ? 'active' : 'inactive'),
        ]}>
        <Section title="Settings">
          <Toggle
            label="Edit mode"
            isOn={editMode}
            onIsOnChange={setEditMode}
          />
        </Section>
        <Section title="Tasks">
          <List.ForEach onDelete={handleDelete} onMove={handleMove}>
            {tasks.map(task => (
              <Label
                key={task.id}
                title={task.title}
                modifiers={[tag(task.id)]}
              />
            ))}
          </List.ForEach>
        </Section>
      </List>
    </Host>
  );
}

Pull to refresh

Use the refreshable modifier to enable pull-to-refresh functionality.

import { useState } from 'react';
import { Host, List, Text, Section } from '@expo/ui/swift-ui';
import { refreshable } from '@expo/ui/swift-ui/modifiers';

export default function RefreshableListExample() {
  const [lastRefresh, setLastRefresh] = useState<Date | null>(null);

  const handleRefresh = async () => {
    // Simulate async data fetching
    await new Promise(resolve => setTimeout(resolve, 1500));
    setLastRefresh(new Date());
  };

  return (
    <Host style={{ flex: 1 }}>
      <List modifiers={[refreshable(handleRefresh)]}>
        <Section title="Data">
          <Text>Pull down to refresh</Text>
          {lastRefresh && (
            <Text>
              Last refresh: {lastRefresh.toLocaleTimeString()}
            </Text>
          )}
        </Section>
      </List>
    </Host>
  );
}

Row styling

Use listRowBackground, listRowSeparator, listRowSeparatorTint, and listRowInsets modifiers to customize individual rows. Use alignmentGuide with the listRowSeparatorLeading guide to set where a row separator starts.

import { Host, List, Text, Section } from '@expo/ui/swift-ui';
import {
  alignmentGuide,
  listRowBackground,
  listRowSeparator,
  listRowSeparatorTint,
  listRowInsets,
} from '@expo/ui/swift-ui/modifiers';

export default function RowStylingExample() {
  return (
    <Host style={{ flex: 1 }}>
      <List>
        <Section title="Styled Rows">
          <Text modifiers={[listRowBackground('blue')]}>
            Blue background
          </Text>
          <Text modifiers={[listRowSeparator('hidden')]}>
            Hidden separator
          </Text>
          <Text modifiers={[listRowSeparatorTint('red')]}>
            Red separator
          </Text>
          <Text modifiers={[listRowInsets({ leading: 40 })]}>
            Extra leading inset
          </Text>
          <Text
            modifiers={[
              listRowInsets({ leading: 0, trailing: 0 }),
            ]}>
            No horizontal insets
          </Text>
          <Text
            modifiers={[
              alignmentGuide('listRowSeparatorLeading', 32),
            ]}>
            Separator starts 32 points in
          </Text>
        </Section>
      </List>
    </Host>
  );
}

Keyboard dismiss behavior

Use the scrollDismissesKeyboard modifier to control how the keyboard is dismissed when scrolling.

import { Host, List, Section, TextField } from '@expo/ui/swift-ui';
import { scrollDismissesKeyboard } from '@expo/ui/swift-ui/modifiers';

export default function KeyboardDismissExample() {
  return (
    <Host style={{ flex: 1 }}>
      <List modifiers={[scrollDismissesKeyboard('interactively')]}>
        <Section title="Form">
          <TextField placeholder="Name" />
          <TextField placeholder="Email" />
          <TextField placeholder="Phone" />
        </Section>
      </List>
    </Host>
  );
}

Header prominence

Use the headerProminence modifier to adjust the visual prominence of section headers.

import { Host, List, Text, Section } from '@expo/ui/swift-ui';
import { headerProminence } from '@expo/ui/swift-ui/modifiers';

export default function HeaderProminenceExample() {
  return (
    <Host style={{ flex: 1 }}>
      <List modifiers={[headerProminence('increased')]}>
        <Section title="Important Section">
          <Text>This section has increased header prominence</Text>
        </Section>
        <Section title="Another Section">
          <Text>Headers are more prominent</Text>
        </Section>
      </List>
    </Host>
  );
}

API

import { List } from '@expo/ui/swift-ui';

Components

List

Type: React.Element<ListProps>

A list component that renders its children using a native SwiftUI List.

ListProps

children

Type: ReactNode

The children elements to be rendered inside the list.

onSelectionChange

Optional • Type: (selection: (string | number)[]) => void

Callback triggered when the selection changes in a list. Returns an array of selected item tags.

selection

Optional • Type: (string | number)[]

The currently selected item tags.

Inherited props

ListForEach

Type: React.Element<ListForEachProps>

A compound component of List that enables item deletion and reordering. This component must be used as a child of List (as List.ForEach).

ListForEachProps

children

Type: ReactNode

The children elements to be rendered inside the List.ForEach.

onDelete

Optional • Type: (indices: number[]) => void

Callback triggered when items are deleted. Receives an array of indices that were deleted.

See: Official SwiftUI documentation.

onMove

Optional • Type: (sourceIndices: number[], destination: number) => void

Callback triggered when items are moved. Receives the source indices and destination index.

See: Official SwiftUI documentation.

Inherited props

Modifiers

Use these modifiers in the modifiers prop of the SwiftUI and Universal version.

Modifiers for List

Modifiers for any view

Sources

The original Expo docs for this page:

  • List (SwiftUI) (source-of-truth/versions/v57.0.0/sdk/ui/swift-ui/list.md)
  • List (Universal) (source-of-truth/versions/v57.0.0/sdk/ui/universal/list.md)

On this page