skip to content

In a React Native SectionList, what shape must the sections prop have, and what do renderItem and renderSectionHeader receive?

level: juniorimportance: must knowfreq 55%

answer

  1. an array of section objects
  2. each section needs data
  3. extra fields ride along
  4. index is within the section
  5. keys are section key plus item key

basics

~20 s

A React Native SectionList takes sections: an array of objects, each with a required data array plus any fields you add, such as title. renderItem receives item, index within its section, section and separators; renderSectionHeader and renderSectionFooter receive the section.

solid answer

~40 s

`sections` is an array of section objects. Each must have `data`, the items of that section, and may carry an optional `key`, per-section overrides (`renderItem`, `ItemSeparatorComponent`, `keyExtractor`) and any fields of your own — typically `title`. The list-level `renderItem({item, index, section, separators})` renders items, where `index` is the position **within the section**, not in the whole list; a section's own `renderItem` overrides it. `renderSectionHeader({section})` and `renderSectionFooter({section})` render above and below each section's items. Keys: a section is keyed by its `key` or else its array index, and each item by `sectionKey:itemKey` using `keyExtractor` (default `item.key`, then `item.id`, then the index), so item keys only need to be unique within their section. For an A-Z music library, sections are the letters and `data` holds the artists under each letter.

code

tsx · 37 lines
tsx
import {SectionList, StyleSheet, Text} from 'react-native';

type Artist = {id: string; name: string};
type LetterSection = {key: string; title: string; data: Artist[]};

export function groupByInitial(artists: Artist[]): LetterSection[] {
  const groups = new Map<string, Artist[]>();
  const sorted = [...artists].sort((a, b) => a.name.localeCompare(b.name));
  for (const artist of sorted) {
    const letter = artist.name.charAt(0).toUpperCase() || '#';
    groups.set(letter, [...(groups.get(letter) ?? []), artist]);
  }
  // Only letters that have artists: empty sections would still show a header.
  return [...groups].map(([letter, data]) => ({key: letter, title: letter, data}));
}

export function ArtistIndex({artists}: {artists: Artist[]}) {
  return (
    <SectionList
      sections={groupByInitial(artists)}
      keyExtractor={artist => artist.id}
      renderItem={({item, index, section}) => (
        <Text style={styles.row}>
          {section.title} {index + 1}. {item.name}
        </Text>
      )}
      renderSectionHeader={({section}) => (
        <Text style={styles.header}>{section.title}</Text>
      )}
    />
  );
}

const styles = StyleSheet.create({
  header: {backgroundColor: '#f4f4f5', paddingHorizontal: 12, paddingVertical: 4, fontWeight: '700'},
  row: {paddingHorizontal: 12, paddingVertical: 10},
});

go deeper

for a junior

Know the shape: an array of sections, each with a required data array plus fields like title. renderItem gets item, index, section and separators; renderSectionHeader gets the section.

for a middle

Explain how the list flattens sections into header, items and footer slots, why index restarts per section, and how keys combine section key and item key.

for a senior

Prevent the production surprises: empty sections still render headers, the empty state needs an empty sections array, and unstable section keys hurt when groups appear or reorder.

for a principal

Decide where grouping happens, on the server or in a selector, so every grouped screen receives stable, pre-sorted sections instead of regrouping in render.

## The data shape `SectionList` is React Native's list for grouped data. Instead of a flat `data` array, it takes `sections`: an array of section objects. For a music library grouped by artist initial: ```tsx const sections = [ {key: 'A', title: 'A', data: [{id: 'a1', name: 'Arcade Fire'}, {id: 'a2', name: 'Aurora'}]}, {key: 'B', title: 'B', data: [{id: 'b1', name: 'Björk'}]}, ]; ``` Each section object has: | Field | Required | Purpose | |---|---|---| | `data` | yes | the items rendered in this section | | `key` | no | the section's identity; defaults to its array index | | `renderItem` | no | overrides the list-level `renderItem` for this section | | `ItemSeparatorComponent` | no | overrides the list-level item separator for this section | | `keyExtractor` | no | overrides the list-level `keyExtractor` for this section | | your own fields | no | anything else, such as `title`, available as `section.title` | ## What the render functions receive - **`renderItem({item, index, section, separators})`** — `item` is the entry from the section's `data`; **`index` is the item's position within its section**, starting at 0 in every section; `section` is the whole section object, so the row can read `section.title`; `separators` offers `highlight`, `unhighlight` and `updateProps` like in a `FlatList`. - **`renderSectionHeader({section})`** — renders at the top of each section. - **`renderSectionFooter({section})`** — renders at the bottom of each section. Per-section `renderItem` is handy when one group renders differently, for example a "Recently played" section of large cards above the alphabetical sections. ## How it is built underneath `SectionList` sits on `VirtualizedSectionList`, which flattens the sections into one virtualized sequence. Every section contributes **a header slot, its items, and a footer slot**, whether or not you provided `renderSectionHeader` or `renderSectionFooter`. Two consequences follow: 1. **Empty sections still render their header and footer.** A section with `data: []` shows its letter with nothing under it, so filter out empty sections before passing them. 2. **`ListEmptyComponent` only appears when there are no slots at all**, which effectively means `sections` is an empty array. A list of sections that all have empty `data` still counts header and footer slots and does not show the empty state. ## How keys are built The docs note that `keyExtractor` "sets keys for each item, but each overall section still needs its own key". In the implementation: - a section's key is `section.key`, or its index as a string when `key` is missing; - a header cell is keyed `sectionKey:header`, a footer `sectionKey:footer`; - an item cell is keyed `sectionKey:itemKey`, where `itemKey` comes from the section's `keyExtractor`, the list's `keyExtractor`, or the default (`item.key`, then `item.id`, then the index). So item keys only need to be unique **within their section**, and a stable `section.key` matters when sections can be re-ordered or inserted — for example a new letter appearing when the user adds their first artist starting with Q. ## A practical checklist 1. Sort items and group them into sections before rendering; `SectionList` does not group for you. 2. Give each section a stable `key`, such as the letter. 3. Drop empty sections unless an empty header is intended. 4. Read section-level data in rows through the `section` argument rather than looking it up again. 5. Remember that `index` restarts at 0 in every section. ## Common mistakes - Passing a flat array to `sections`, or naming the field `items` instead of `data`. - Using `index` from `renderItem` as a global position. - Expecting `ListEmptyComponent` when every section is empty. - Relying on index section keys while sections come and go.

  • Why does a SectionList show a letter header with no rows under it?
    Each section always contributes a header slot and a footer slot, whatever its `data` contains, so a section with `data: []` still renders its header. Filter out empty sections when building `sections`, unless an empty group is meant to be visible.
  • Do two artists in different sections need globally unique keys?
    No. An item's cell key is the section key joined with the item key, such as `A:a1`, so item keys only need to be unique within their section. Section keys, from `section.key` or the index, must be unique across the list.
  • How can one section render its rows differently from the rest?
    Give that section object its own `renderItem`; it overrides the list-level `renderItem` for that section only. The same goes for `ItemSeparatorComponent` and `keyExtractor`, which can also be set per section.

saying these in an interview costs you the question

  • SectionList takes a flat data array plus a groupBy function.
  • The index passed to renderItem counts items across the whole list.
  • A section with an empty data array renders nothing at all.
  • Item keys in a SectionList must be unique across every section.
  • ListEmptyComponent shows whenever every section has empty data.