>_ DevTrendsen

Language

Home

Languages

Sections

Frontend Backend Mobile DevOps AI / ML GameDev Blockchain Embedded Security
TypeScript

Fast Lists in React Native Without Sizing Workarounds or Native Modules

If you've ever built a complex interface in React Native, you've probably cursed at lists. The standard FlatList starts dropping FPS on long lists and clogs up memory. FlashList from Shopify is noticeably faster thanks to component reuse, but try feeding it elements of unpredictable height like posts with varying amounts of text and images. Micro-freezes during fast scrolling or layout jittering begin when the list tries to guess cell sizes through estimatedItemSize.

Recently, the LegendApp team released the Legend List library. It's a list component written entirely in TypeScript, without a single line of native code, that solves exactly this dynamic sizing headache.

The Concept

The main goal of the project is to provide a replacement for FlatList and FlashList with a familiar API, but without performance drops on variable-height elements. The developers deliberately used only pure JavaScript and TypeScript. No native modules need to be linked. This simplifies building for Expo Go and web versions, and also reduces the risk of breaking something when updating the React Native version.

The project is lightweight and works as a drop-in replacement. If you already have a screen written with FlatList, a basic migration only requires changing the import and component name.

What the Library Can Do

The developers focused on typical problems with lists in mobile apps. Here are four things that are noticeably more convenient here:

  1. Dynamic height out of the box. You don't need to pre-calculate exact card dimensions or pass approximate estimates. The list adapts to content sizes on its own without render lag.

  2. Proper chat interface. The classic trick for creating a messaging screen is to flip the list through inverted. This causes weird bugs with keyboard animations, inverted scrolling, and positioning. Legend List has a alignItemsAtEnd prop that aligns content to the bottom edge without inversion, and maintainScrollAtEnd that keeps scroll at the last message when new data arrives.

  3. Bidirectional infinite scroll. Loading data up and down works without white screen flashes and abrupt jumps in viewport position.

  4. Controlled cell recycling. Through the recycleItems prop, you can enable or disable component reuse. If a complex local useState is stored inside a list element, reuse sometimes leads to unpleasant side effects with displaying someone else's state. In Legend List, recycling is disabled by default for safety, but you can enable it with a single line for maximum speed.

What It Looks Like in Code

Installation is standard:

npm install @legendapp/list
# или через bun / yarn
bun add @legendapp/list

We import the component from the runtime subfolder and pass familiar props:

import React, { useRef } from "react"
import { View, Image, Text, StyleSheet } from "react-native"
import { LegendList, LegendListRef, LegendListRenderItemProps } from "@legendapp/list/react-native"

interface PostItem {
  id: string
  author: string
  content: string
  photoUrl?: string
}

export const FeedScreen = ({ posts }: { posts: PostItem[] }) => {
  const listRef = useRef<LegendListRef | null>(null)

  const renderItem = ({ item }: LegendListRenderItemProps<PostItem>) => {
    return (
      <View style={styles.card}>
        <Text style={styles.author}>{item.author}</Text>
        <Text style={styles.text}>{item.content}</Text>
        {item.photoUrl && (
          <Image source={{ uri: item.photoUrl }} style={styles.image} />
        )}
      </View>
    )
  }

  return (
    <LegendList
      data={posts}
      renderItem={renderItem}
      keyExtractor={(item) => item.id}
      recycleItems={true}
      maintainVisibleContentPosition
      ref={listRef}
    />
  )
}

const styles = StyleSheet.create({
  card: { padding: 16, borderBottomWidth: 1, borderColor: "#eee" },
  author: { fontWeight: "bold", marginBottom: 4 },
  text: { fontSize: 14, lineHeight: 20 },
  image: { width: "100%", height: 200, marginTop: 8, borderRadius: 8 },
})

The maintainVisibleContentPosition prop keeps content on screen if element sizes above the visible area change or new data arrives.

Limitations and Plans

The repository is young, it appeared at the end of 2024. In the roadmap, the authors openly state what's not there yet:

  • Masonry grids (tiles of varying heights)
  • Sticky headers
  • Column spans
  • Separate item types (getItemType)

If your screen relies on a complex grid with sticky sections, it's too early to migrate.

Who Should Try It

The library is useful for those tired of fighting interface jumps in chats, news feeds, or comment lists. If FlashList jitters on your dynamic cards and FlatList doesn't provide the smoothness you need, install @legendapp/list in a test branch. It takes a couple of minutes to check since you won't need to change component structure or write native wrappers.

Related projects