SectionList & Dados Agrupados
Cabeçalhos, rodapés e cabeçalhos de seção fixos para contatos em ordem alfabética, configurações categorizadas e feeds de linha do tempo.
Busque em todas as páginas da documentação
Cabeçalhos, rodapés e cabeçalhos de seção fixos para contatos em ordem alfabética, configurações categorizadas e feeds de linha do tempo.
Cartão de receita de referência rápida - pronto para copiar e colar.
import { SectionList, StyleSheet, Text, View } from "react-native";
interface Contact {
id: string;
name: string;
}
interface ContactSection {
title: string;
data: Contact[];
}
const SECTIONS: ContactSection[] = [
{
title: "A",
data: [
{ id: "a1", name: "Alex Rivera" },
{ id: "a2", name: "Avery Chen" },
],
},
{
title: "B",
data: [{ id: "b1", name: "Blake Morgan" }],
},
];
export function ContactSectionList() {
return (
<SectionList
sections={SECTIONS}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={styles.row}>
<Text style={styles.name}>{item.name}</Text>
</View>
)}
renderSectionHeader={({ section }) => (
<View style={styles.header}>
<Text style={styles.headerText}>{section.title}</Text>
</View>
)}
stickySectionHeadersEnabled
ItemSeparatorComponent={() => <View style={styles.separator} />}
SectionSeparatorComponent={() => <View style={styles.sectionGap} />}
/>
);
}
const styles = StyleSheet.create({
header: {
backgroundColor: "#f1f5f9",
paddingHorizontal: 16,
paddingVertical: 6,
},
headerText: { fontSize: 13, fontWeight: "700", color: "#475569" },
row: { paddingHorizontal: 16, paddingVertical: 12, backgroundColor: "#fff" },
name: { fontSize: 16, color: "#0f172a" },
separator: { height: StyleSheet.hairlineWidth, backgroundColor: "#e2e8f0" },
sectionGap: { height: 8 },
});Quando usar isso: Dados que naturalmente se agrupam em seções rotuladas - contatos por letra, configurações por categoria, atividade por data.
import { useMemo } from "react";
import { SectionList, StyleSheet, Text, View, type SectionListData } from "react-native";
interface SettingItem {
id: string;
label: string;
value?: string;
}
interface SettingSection extends SectionListData<SettingItem> {
title: string;
data: SettingItem[];
}
const SETTINGS: SettingItem[] = [
{ id: "profile", label: "Profile", value: "Alex Rivera" },
{ id: "email", label: "Email", value: "alex@example.com" },
{ id: "push", label: "Push notifications", value: "On" },
{ id: "dark", label: "Dark mode", value: "System" },
{ id: "language", label: "Language", value: "English" },
{ id: "privacy", label: "Privacy policy" },
{ id: "terms", label: "Terms of service" },
];
function groupSettings(items: SettingItem[]): SettingSection[] {
const account = items.filter((i) => ["profile", "email"].includes(i.id));
const preferences = items.filter((i) => ["push", "dark", "language"].includes(i.id));
const legal = items.filter((i) => ["privacy", "terms"].includes(i.id));
return [
{ title: "Account", data: account },
{ title: "Preferences", data: preferences },
{ title: "Legal", data: legal },
].filter((section) => section.data.length > 0);
}
function SectionHeader({ title }: { title: string }) {
return (
<View style={styles.sectionHeader}>
<Text style={styles.sectionHeaderText}>{title}</Text>
</View>
);
}
function SettingRow({ item }: { item: SettingItem }) {
return (
<View style={styles.row}>
<Text style={styles.label}>{item.label}</Text>
{item.value ? <Text style={styles.value}>{item.value}</Text> : null}
</View>
);
}
export default function SettingsScreen() {
const sections = useMemo(() => groupSettings(SETTINGS), []);
return (
<SectionList
sections={sections}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <SettingRow item={item} />}
renderSectionHeader={({ section }) => <SectionHeader title={section.title} />}
renderSectionFooter={({ section }) =>
section.title === "Legal" ? (
<Text style={styles.footerNote}>Version 2.4.1</Text>
) : null
}
stickySectionHeadersEnabled
ItemSeparatorComponent={() => <View style={styles.itemSeparator} />}
contentContainerStyle={styles.list}
/>
);
}
const styles = StyleSheet.create({
list: { paddingBottom: 32 },
sectionHeader: {
backgroundColor: "#f8fafc",
paddingHorizontal: 16,
paddingTop: 16,
paddingBottom: 6,
},
sectionHeaderText: {
fontSize: 12,
fontWeight: "700",
letterSpacing: 0.6,
textTransform: "uppercase",
color: "#64748b",
},
row: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: 16,
paddingVertical: 14,
backgroundColor: "#fff",
},
label: { fontSize: 16, color: "#0f172a" },
value: { fontSize: 15, color: "#64748b" },
itemSeparator: {
height: StyleSheet.hairlineWidth,
backgroundColor: "#e2e8f0",
marginLeft: 16,
},
footerNote: {
textAlign: "center",
fontSize: 12,
color: "#94a3b8",
paddingVertical: 24,
},
});O que isso demonstra:
useMemo em seções { title, data }.renderSectionHeader renderiza um componente de cabeçalho leve para cada seção.renderSectionFooter adiciona uma nota de versão apenas após a última seção.stickySectionHeadersEnabled mantém os títulos das seções visíveis ao rolar os itens.ItemSeparatorComponent recuado (via marginLeft) alinha os divisores com o conteúdo da linha, não com a margem da seção.SectionList estende o mesmo motor de virtualização do FlatList, mas itera sobre seções e depois sobre itens dentro de cada seção.{ data: ItemT[], ...customFields } - comumente title, mas você pode anexar key, footer ou metadados.renderSectionHeader e renderSectionFooter são chamados para o "chrome" da seção; eles não são virtualizados da mesma forma que as linhas (cabeçalhos podem ficar fixos).stickySectionHeadersEnabled (padrão true em listas estilo iOS) fixa o cabeçalho da seção atual no topo até que a próxima seção o substitua.keyExtractor é executado nos itens, não nas seções - a identidade da seção vem do índice ou de um campo key no objeto da seção.data: []) ainda renderizam cabeçalhos, a menos que você as filtre antes de passar sections.interface TimelineItem {
id: string;
body: string;
at: string;
}
interface TimelineSection {
title: string; // exibido no cabeçalho
key?: string; // chave de seção estável opcional
data: TimelineItem[];
}
// Agrupa itens planos por rótulo de data
function groupByDate(items: TimelineItem[]): TimelineSection[] {
const map = new Map<string, TimelineItem[]>();
for (const item of items) {
const day = item.at.slice(0, 10); // "2026-07-08"
const bucket = map.get(day) ?? [];
bucket.push(item);
map.set(day, bucket);
}
return Array.from(map.entries()).map(([title, data]) => ({ title, data }));
}| Prop | Renderiza | Conteúdo Típico |
|---|---|---|
renderSectionHeader | Topo de cada seção | Rótulo de letra, data, categoria |
renderSectionFooter | Fundo de cada seção | Resumo da seção, espaçamento, "Mostrar mais" |
ListHeaderComponent | Acima de todas as seções | Título da tela, barra de pesquisa |
ListFooterComponent | Abaixo de todas as seções | Spinner de carregar mais, texto legal |
SectionSeparatorComponent | Entre seções | Espaço extra entre grupos |
ItemSeparatorComponent | Entre itens em uma seção | Divisores finos |
stickySectionHeadersEnabled={true} - Comportamento estilo Contatos do iOS; o cabeçalho fica fixo até ser substituído.stickySectionHeadersEnabled={false} quando os cabeçalhos forem altos (imagens, gráficos) - cabeçalhos grandes fixos obscurecem as linhas.paddingTop a renderSectionHeader usando useSafeAreaInsets() quando a lista estiver em tela cheia.Para saltos de A a Z, combine SectionList com um trilho de índice posicionado absolutamente que chama scrollToLocation:
sectionListRef.current?.scrollToLocation({
sectionIndex: 2,
itemIndex: 0,
animated: true,
viewOffset: 0,
});scrollToLocation precisa de layout confiável - forneça getItemLayout quando as alturas das linhas forem fixas.onScrollToIndexFailed para tentar novamente após mais linhas serem renderizadas.import type { SectionListData, SectionListRenderItem } from "react-native";
interface Place {
id: string;
name: string;
}
interface PlaceSection extends SectionListData<Place> {
title: string;
data: Place[];
}
const renderItem: SectionListRenderItem<Place, PlaceSection> = ({ item, section }) => (
<PlaceRow place={item} sectionTitle={section.title} />
);
<SectionList<Place, PlaceSection>
sections={sections}
renderItem={renderItem}
renderSectionHeader={({ section }) => <Header title={section.title} />}
/>SectionList<ItemT, SectionT> quando as seções carregam campos personalizados além de data.SectionListData<T> é o tipo base para objetos de seção.| Parâmetro | Tipo | Descrição |
|---|---|---|
sections | readonly SectionT[] | Array de seções, cada uma com data: ItemT[] |
renderItem | SectionListRenderItem<ItemT, SectionT> | Renderiza um item; recebe section nas informações |
renderSectionHeader | ({ section }) => ReactElement | Cabeçalho acima dos itens de cada seção |
renderSectionFooter | ({ section }) => ReactElement | Rodapé abaixo dos itens de cada seção |
keyExtractor | (item: ItemT, index: number) => string | Chave estável por item |
stickySectionHeadersEnabled | boolean | Fixa cabeçalhos durante a rolagem (padrão true) |
getItemLayout | (data, index) => { length, offset, index } | Altura fixa do item para rolagem para localização |
Quebras de seção manuais dentro de FlatList - Cabeçalhos falsos como tipos de linha especiais complicam a virtualização, o comportamento fixo e os separadores. Correção: Use SectionList quando os dados estiverem agrupados.
Reconstrução de seções a cada renderização - sections={groupData(items)} aloca novos arrays e força a atualização completa da lista. Correção: useMemo(() => groupData(items), [items]).
Chaves de item duplicadas entre seções - keyExtractor deve ser globalmente único em todas as seções, não apenas dentro de uma. Correção: Prefixe as chaves: `${section.title}-${item.id}` apenas se os ids não forem globalmente únicos (prefira ids globais).
Arrays data vazios ainda mostram cabeçalhos - Os usuários veem "Today" sem linhas abaixo. Correção: .filter((s) => s.data.length > 0) antes de passar sections.
Cabeçalhos de seção pesados - Imagens grandes ou gráficos em renderSectionHeader causam lentidão quando cabeçalhos fixos se empilham. Correção: Mantenha os cabeçalhos compactos; mova conteúdo rico para a primeira linha ou para um ListHeaderComponent.
scrollToLocation sem getItemLayout - Linhas de altura variável tornam a matemática do índice incorreta e acionam onScrollToIndexFailed. Correção: Alturas fixas + getItemLayout, ou trate o callback de falha com uma nova tentativa.
Esquecer extraData para estado da linha - Mesmo comportamento de PureComponent que FlatList - o estado de seleção fora de sections não atualizará as linhas. Correção: Passe extraData quando a interface do usuário da linha depender de estado externo.
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
SectionList | Grupos rotulados, cabeçalhos fixos, listas de alfabeto | Sequência plana única sem "chrome" de seção |
FlatList + linhas de cabeçalho | Uma ou duas quebras visuais, sem cabeçalhos fixos | Muitas seções com comportamento fixo estilo iOS |
FlashList com stickyHeaderIndices | Feeds agrupados críticos para desempenho | Você precisa da API renderSectionHeader de primeira classe |
ScrollView + mapas aninhados | Configurações estáticas curtas (< 30 linhas no total) | Listas de contatos longas - sem virtualização |
groupBy no estado | Seções raramente mudam | Reagrupamento a cada pressionamento de tecla sem useMemo |
FlatList virtualiza um único array data. SectionList virtualiza múltiplos arrays data agrupados sob cabeçalhos de seção (e rodapés opcionais). Use SectionList quando o agrupamento faz parte do modelo de dados, não uma linha de cabeçalho única.
Um array de objetos com pelo menos data: Item[]. Adicione title, key ou campos personalizados. Exemplo: [{ title: "A", data: [...] }, { title: "B", data: [...] }].
Apenas a itens. Dê a cada seção uma propriedade key se precisar de identidade de seção estável: { key: "account", title: "Account", data: [...] }.
stickySectionHeadersEnabled (padrão true) mantém o cabeçalho da seção atual fixo no topo da lista até que o cabeçalho da próxima seção apareça. Desative para cabeçalhos altos que cobririam muito conteúdo.
Execute um redutor groupBy em useMemo - agrupe por data, categoria ou primeira letra. Ordene as chaves das seções, depois mapeie para { title, data }. Não agrupe inline em JSX.
Sim - use uma união discriminada para itens e ramifique em renderItem. Mantenha keyExtractor estável entre as variantes. Para layouts de linha muito diferentes, considere listas de seção separadas ou FlashList com tipos heterogêneos.
Renderiza entre seções (após o último item da seção N, antes do cabeçalho da seção N+1). Use para um espaço vertical extra entre grupos. ItemSeparatorComponent renderiza entre itens dentro de uma seção.
Use ListHeaderComponent - ele rola com o conteúdo e fica acima da primeira seção. Para uma barra de pesquisa fixa, renderize-a fora da SectionList em um layout de coluna (flex: 1 na lista).
Encontre sectionIndex para a letra alvo, depois chame ref.scrollToLocation({ sectionIndex, itemIndex: 0 }). Forneça getItemLayout para alturas de linha fixas e trate onScrollToIndexFailed.
Use-o para resumos por seção ("3 itens"), espaçamento ou CTAs. Para conteúdo de rodapé em todo o aplicativo (versão, carregar mais), prefira ListFooterComponent.
Os IDs dos itens devem ser únicos em todas as seções. Se a API reutiliza IDs por seção, prefira prefixar: keyExtractor={(item, index) => `${item.id}-${index}`} - mas IDs de servidor estáveis são fortemente preferidos.
Sim - adicione itens à última seção ou adicione novas seções no seu manipulador de paginação. Passe onEndReached e proteja com um sinalizador de carregamento. Veja Infinite Scroll & Pagination.
As mesmas regras se aplicam: renderItem estável, linhas memo, extraData, evite separadores anônimos. Os cabeçalhos de seção são renderizados novamente quando a referência sections muda - memoize a estrutura agrupada.
Sim - React Native implementa cabeçalhos fixos em ambas as plataformas. Teste em dispositivos Android com níveis de API variados; a cor de fundo do cabeçalho deve ser opaca para evitar que as linhas transpareçam.
Veja FlatList Recipes para keyExtractor, separadores e estados vazios que se aplicam igualmente aos itens do SectionList.
keyExtractor, separadores e ListEmptyComponent compartilhados por itens de seçãoScrollView, FlatList e SectionListsectionsSectionList<Item, Section>Versões do Stack: Esta página foi escrita para React 19.2.3, React Native 0.86.0 e Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026