Skip to content
React NativeBetaMobile only

ListItem & List

Rows for settings, contacts and inboxes: leading icon, title and description, trailing value, control or chevron.

Beta. React Native support is in beta. APIs may change or break between prereleases (npm tag native).

On this page
Web preview — real device may differ
tsx
import { useState } from "react";
import { Text, View } from "react-native";
import { Avatar } from "@/component-lib/avatar";
import { Badge } from "@/component-lib/badge";
import { useKitTheme } from "@/component-lib/shared/native";
import { Switch } from "@/component-lib/switch";
import { List, ListItem, ListSection } from "@/component-lib/list-item";
import { Section } from "./layout"; // Section and Row: page layout for the demo
 
/** A coloured rounded square with a letter: a settings-style icon without SVG. */
function Tile({ color, letter }: { color: string; letter: string }) {
  return (
    <View style={{ width: 28, height: 28, borderRadius: 7, backgroundColor: color, alignItems: "center", justifyContent: "center" }}>
      <Text style={{ color: "#fff", fontWeight: "800", fontSize: 13 }}>{letter}</Text>
    </View>
  );
}
 
export function ListItemDemo() {
  const { tokens } = useKitTheme();
  const [push, setPush] = useState(true);
  const [taps, setTaps] = useState(0);
  return (
    <>
      <Section title="ListSection · inset (settings)">
        <ListSection header="Account" footer={`Opened ${taps} time${taps === 1 ? "" : "s"}. Rows with a chevron open a screen.`}>
          <ListItem leading={<Tile color="#2563eb" letter="L" />} title="Language" value="English" chevron onPress={() => setTaps((n) => n + 1)} />
          <ListItem
            leading={<Tile color="#15803d" letter="N" />}
            title="Push notifications"
            trailing={<Switch aria-label="Push notifications" checked={push} onCheckedChange={setPush} />}
          />
          <ListItem leading={<Tile color="#b45309" letter="₹" />} title="Currency" value="INR (₹)" chevron onPress={() => setTaps((n) => n + 1)} />
          <ListItem title="Sign out" destructive onPress={() => setTaps((n) => n + 1)} />
        </ListSection>
      </Section>
      <Section title="List · plain (inbox)">
        <List style={{ marginHorizontal: -16 }}>
          <ListItem
            leading={<Avatar name="Anand Remanan" size="md" />}
            title="Anand Remanan"
            description="Invoice INV-2041 is ready for review. The totals changed after the GST update."
            trailing={<Badge variant="accent">New</Badge>}
            onPress={() => setTaps((n) => n + 1)}
          />
          <ListItem
            leading={<Avatar name="Gita Hari" size="md" />}
            title="Gita Hari"
            description="Shared the Q3 ledger with you."
            value="09:41"
            onPress={() => setTaps((n) => n + 1)}
          />
          <ListItem leading={<Avatar name="Old Account" size="md" />} title="Old account" description="Archived" disabled />
        </List>
        <Text style={{ color: tokens.muted, fontSize: tokens.fontSize - 2 }}>Long descriptions stop at two lines.</Text>
      </Section>
    </>
  );
}

Install

npx @grampro/kit@native add ListItem

Usage

tsx
import { ListItem, ListSection } from "@/component-lib/list-item";
 
<ListSection header="Account" footer="Signed in as anand@example.com">
  <ListItem title="Language" value="English" chevron onPress={openLanguage} />
  <ListItem title="Notifications" trailing={<Switch checked={on} onCheckedChange={setOn} />} />
  <ListItem title="Sign out" destructive onPress={signOut} />
</ListSection>

Props

ListItemProps

PropTypeDescription
leadingReactNodeIcon or avatar before the text.
title (required)ReactNode
descriptionReactNodeSupporting text under the title.
numberOfLinesnumberLines of description before truncating. Default 2.
valueReactNodeSettings-style value text at the end, e.g. "English".
trailingReactNodeAnything at the end: a Switch, a Badge, a button.
chevronbooleanA disclosure chevron at the end: the row opens something.
destructivebooleanRed text for delete / sign out rows.
disabledboolean
onPress(event: GestureResponderEvent) => void
onLongPress(event: GestureResponderEvent) => void
styleStyleProp<ViewStyle> | ((state: { pressed: boolean }) => StyleProp<ViewStyle>)
stylesPartial<{ leading: StyleProp<ViewStyle>; title: StyleProp<TextStyle>; description: Styl…

ListProps

PropTypeDescription
variantListVariantplain: full-width rows. inset: a rounded card of rows (iOS settings). Default plain.
dividedbooleanHairlines between rows, inset past the leading slot. Default true.
styleStyleProp<ViewStyle>
childrenReactNode

ListSectionProps

PropTypeDescription
headerReactNodeSmall caps label above the group.
footerReactNodeExplanatory text below the group.
variantListVariant
dividedboolean
styleStyleProp<ViewStyle>
childrenReactNode

Notes

  • Rows are at least 44dp tall (56dp with a description).
  • A pressable row with a chevron is announced as a link, otherwise as a button.
  • ListSection defaults to the rounded inset look of iOS settings.
  • Mobile only: there is no web version of this component.