Skip to content

Instantly share code, notes, and snippets.

@robinheinze
Last active March 14, 2024 15:09
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save robinheinze/19c4500b22a0c01d359f560d17612258 to your computer and use it in GitHub Desktop.
Save robinheinze/19c4500b22a0c01d359f560d17612258 to your computer and use it in GitHub Desktop.
Multi-column SectionList using FlatList

First, let's assemble the data. Say we have two sets of objects: Fruits and Vegetables.

const fruits = [
  {
    name: 'Apple',
    color: 'Green'
  },
  {
    name: 'Banana',
    color: 'Yellow'
  },
  {
    name: 'Strawberry',
    color: 'Red'
  },
  {
    name: 'Blueberry',
    color: 'Blue'
  }
]
const vegetables = [
  {
    name: 'Carrot',
    color: 'Orange'
  },
  {
    name: 'Cabbage',
    color: 'Purple'
  }
]

For a regular section list with no columns, our sections array would look like this. Note how each section has a data attribute, which is an array of our fruits or vegetables.:

const plainOldsections = [
  {
    title: 'Vegetables',
    key: 'vegetables',
    data: [
      {
        name: 'Carrot',
        color: 'Orange'
      },
      {
        name: 'Cabbage',
        color: 'Purple'
      }
    ],
  },
  {
    title: 'Fruits',
    key: 'fruits',
    data: [
      {
        name: 'Apple',
        color: 'Green'
      },
      {
        name: 'Banana',
        color: 'Yellow'
      },
      {
        name: 'Strawberry',
        color: 'Red'
      },
      {
        name: 'Blueberry',
        color: 'Blue'
      }
    ],
  },
]

But for our multi-colum section list, we have to get a bit tricky:

const multiColumnSections = [
  {
    title: 'Vegetables',
    key: 'vegetables',
    data: [
     {
       list: [
          {
            name: 'Carrot',
            color: 'Orange'
          },
          {
            name: 'Cabbage',
            color: 'Purple'
          }
        ]
      }
    ]
  },
  {
    title: 'Fruits',
    key: 'fruits',
    data: [
      {
        list: [
          {
            name: 'Apple',
            color: 'Green'
          },
          {
            name: 'Banana',
            color: 'Yellow'
          },
          {
            name: 'Strawberry',
            color: 'Red'
          },
          {
            name: 'Blueberry',
            color: 'Blue'
          }
        ],
      }
    ]
  },
]

Note how the data attribute in each section is now an array with one item in it. That one item is just an object with a list attribute which contains our actual data.

What we're going to do, is pass that list into a FlatList for each section.

So here's what the final product looks like:

import * as React from "react"
import { Text, View, FlatList, SectionList } from "react-native"

const sections = [
  {
    title: "Vegetables",
    key: "vegetables",
    data: [
     {
       key: "vegetables",
       list: [
          {
            name: "Carrot",
            color: "Orange",
          },
          {
            name: "Cabbage",
            color: "Purple",
          },
        ],
      },
    ],
  },
  {
    title: "Fruits",
    key: "fruits",
    data: [
      {
        key: 'fruits',
        list: [
          {
            name: "Apple",
            color: "Green",
          },
          {
            name: "Banana",
            color: "Yellow",
          },
          {
            name: "Strawberry",
            color: "Red",
          },
          {
            name: "Blueberry",
            color: "Blue",
          },
        ],
      },
    ],
  },
]

export class MultiColumnExample extends React.Component<{}, {}> {
  renderSection = ({ item }) => {
    return (
      <FlatList
        data={item.list}
        numColumns={3}
        renderItem={this.renderListItem}
        keyExtractor={this.keyExtractor}
      />
    )
  }

  renderSectionHeader = ({ section }) => {
    return <Text>{section.title}</Text>
  }

  renderListItem = ({ item }) => {
      return (
        <View style={{height: 50, width: 100, borderColor: "green", borderWidth: 1 }}>
          <Text>{item.name}</Text>
          <Text>{item.color}</Text>
        </View>
      )
    }

  keyExtractor = (item) => {
    return item.name
  }

  render () {
    return (
      <SectionList
        sections={sections}
        renderSectionHeader={this.renderSectionHeader}
        renderItem={this.renderSection}
      />
    )

  }
}

@dwilt
Copy link

dwilt commented Sep 30, 2018

Clever idea with the embedded single object in the data array. Thanks for posting.

@roshangm1
Copy link

roshangm1 commented Oct 11, 2018

@robinheinze

I tried to implement this for making non-column section list. I want to make a horizontal list with vertical Section Headers. What I did is:
Non horizontal sectionlist with it's item as a horizontal flatlist.

<SectionList
          sections={vendors}
          renderSectionHeader={({ section }) => (
            <Text> {section.category}</Text>
          )}
          renderItem={({ section }) => (
            <FlatList
              data={section.data}
              horizontal={true}
              renderItem={({ item }) => <Text>{item.name}</Text>}
            />
          )}
        />

But the problem is multiple flatlists are rendered inside a section. For example, if I had two data to render, I will get two different flatlists and similarly, three flatlists with three data.

Can you help me on this ?

@mehdichk
Copy link

doesnt fckin work :)
Tried everything and I get the error "undefined is not an object"...
Im lost.

@matteocollina
Copy link

It works thank you even if I don't like the design of this solution but I think that it is currently the only one.

@codeserk
Copy link

codeserk commented Jul 7, 2021

@robinheinze Hello! I was facing problems trying to get multiple columns work with SectionList and I found this and similar solutions. But is it wise to apply this? I mean, we are creating one FlatList per section, won't that has some kind of impact in performance?

Thanks for the Gist with detailed explanation btw :)

@abhishrek
Copy link

I implemented it, and it works, but there is a big problem with it.

A regular sectionlist crashes if the list is huge and getItemLayout() is not implemented while calling scrollToLocation()
implementing getItemLayout() is difficult if the section has a Flatlist in it.

@anrodrigues0
Copy link

anrodrigues0 commented Mar 12, 2023

i solved this problem in this way

const renderItemList: SectionListRenderItem<TicketTypes> = ({
  index,
  section,
}) => {

  if (index % 2 !== 0) {
    return null;
  }

  const item = section.data[index];
  const nextItem = section.data[index + 1];
  return (
    <View
      style={{
        flexDirection: 'row',
        justifyContent: 'space-between',
        padding: 10,
      }}>
      <CardTicket data={item} />
      {nextItem ? <CardTicket data={nextItem} /> : <View style={{flex: 1}} />}
    </View>
  );
};

return (
  <Container>
    <SectionList
      sections={MockTickets}
      keyExtractor={({id}) => id}
      renderItem={renderItemList}
      renderSectionHeader={({section: {category}}) => (
        <Text style={{backgroundColor: 'gray', paddingVertical: 10}}>
          {category}
        </Text>
      )}
    />
  </Container>
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment