Vue NativeVue Native
Guide
Components
Composables
Navigation
Architecture
  • iOS
  • Android
  • macOS
GitHub
Guide
Components
Composables
Navigation
Architecture
  • iOS
  • Android
  • macOS
GitHub
  • Layout

    • VView
    • VScrollView
    • VSafeArea
    • VKeyboardAvoiding
  • Text & Input

    • VText
    • VInput
  • Interactive

    • VButton
    • VPressable
    • VSwitch
    • VSlider
    • VSegmentedControl
    • VCheckbox
    • VRadio
    • VDropdown
    • VRefreshControl
  • Media

    • VImage
    • VSVG
    • VWebView
    • VVideo
  • Lists

    • VList
    • VFlatList
    • VSectionList
  • Feedback

    • VActivityIndicator
    • VProgressBar
    • VAlertDialog
    • VActionSheet
    • VModal
    • VErrorBoundary
  • Transition & State

    • VTransition & VTransitionGroup
    • KeepAlive
    • VSuspense
  • Navigation

    • VNavigationBar
    • VTabBar
    • VDrawer
  • System

    • VStatusBar
    • VPicker
  • macOS

    • VToolbar
    • VSplitView
    • VOutlineView

VFlatList

A high-performance virtualized list for large datasets. Unlike VList (which uses native UITableView / RecyclerView), VFlatList only creates native views for items currently visible on screen plus a configurable buffer -- reducing memory usage from O(n) to O(visible).

Uses VScrollView internally with absolutely-positioned items. The scroll position drives which items are mounted.

Usage

VFlatList uses a :data prop and an #item slot to render each row. For the fastest, no-measurement virtualization math, set a fixed itemHeight. If you omit it, VFlatList falls back to a variable-height mode: it renders each item at estimatedItemHeight until the native side reports its real measured height via an itemLayout event, then repositions using that measurement.

<script setup>
import { ref } from '@thelacanians/vue-native-runtime'

const items = ref(
  Array.from({ length: 10000 }, (_, i) => ({ id: i + 1, title: `Item ${i + 1}` }))
)
</script>

<template>
  <VFlatList
    :data="items"
    :itemHeight="52"
    :keyExtractor="item => item.id"
    :style="{ flex: 1 }"
  >
    <template #item="{ item }">
      <VView :style="{ padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee' }">
        <VText>{{ item.title }}</VText>
      </VView>
    </template>
  </VFlatList>
</template>

Props

PropTypeDefaultDescription
dataArrayrequiredThe array of items to render
itemHeightnumber—Fixed height (in points) for each item -- the fast path for virtualization math. Omit for variable-height lists (see estimatedItemHeight).
estimatedItemHeightnumber44Estimated height for items whose real height isn't yet measured, used for variable-height lists (when itemHeight is omitted) until native reports each item's actual height via the itemLayout event. Ignored when itemHeight is set.
keyExtractor(item: T, index: number) => string | numberitem.id ?? item.key ?? indexFunction that returns a unique key for each item
renderItem(info: { item: T, index: number }) => VNode--Render function alternative to the #item slot
windowSizenumber3Number of viewport-heights to render above and below the visible area. Higher values reduce blank flashes during fast scrolling but use more memory.
styleViewStyle{}Styles for the outer scroll container
showsScrollIndicatorbooleantrueShow/hide the vertical scroll indicator
bouncesbooleantrueBounce at scroll boundaries (iOS only)
headerHeightnumber0Height in points of the #header slot content. Required for correct virtualization math when using a header.
endReachedThresholdnumber0.5How far from the end (in viewport fractions) to trigger @endReached. 0.5 = trigger when within 50% of a viewport from the bottom.

Slots

SlotPropsDescription
#item{ item, index }Render each list item. Required unless renderItem prop is used.
#header--Content rendered above the list items
#empty--Content shown when data is an empty array

Events

EventPayloadDescription
@scroll{ x, y, contentWidth, contentHeight, layoutWidth, layoutHeight }Fires as the user scrolls
@endReached--Fires when scrolled near the bottom (controlled by endReachedThreshold). Use for infinite scroll / pagination.

Infinite scroll example

<script setup>
import { ref } from '@thelacanians/vue-native-runtime'

const items = ref(Array.from({ length: 50 }, (_, i) => ({ id: i + 1, title: `Item ${i + 1}` })))
const loading = ref(false)

async function loadMore() {
  if (loading.value) return
  loading.value = true
  const start = items.value.length
  const more = Array.from({ length: 20 }, (_, i) => ({
    id: start + i + 1,
    title: `Item ${start + i + 1}`,
  }))
  items.value = [...items.value, ...more]
  loading.value = false
}
</script>

<template>
  <VFlatList
    :data="items"
    :itemHeight="52"
    :keyExtractor="item => item.id"
    :style="{ flex: 1 }"
    @endReached="loadMore"
  >
    <template #item="{ item }">
      <VView :style="{ padding: 16 }">
        <VText>{{ item.title }}</VText>
      </VView>
    </template>
  </VFlatList>
</template>

Header and empty state

<template>
  <VFlatList
    :data="items"
    :itemHeight="52"
    :keyExtractor="item => item.id"
    :style="{ flex: 1 }"
  >
    <template #header>
      <VView :style="{ padding: 16 }">
        <VText :style="{ fontSize: 24, fontWeight: 'bold' }">My Items</VText>
      </VView>
    </template>

    <template #item="{ item }">
      <VView :style="{ padding: 16 }">
        <VText>{{ item.title }}</VText>
      </VView>
    </template>

    <template #empty>
      <VView :style="{ padding: 40, alignItems: 'center' }">
        <VText :style="{ color: '#999' }">No items yet</VText>
      </VView>
    </template>
  </VFlatList>
</template>

Using renderItem prop

For programmatic rendering, use the renderItem prop instead of the #item slot:

<script setup>
import { h } from '@thelacanians/vue-native-runtime'

const data = Array.from({ length: 10000 }, (_, i) => ({ id: i, title: `Item ${i}` }))

function renderItem({ item, index }) {
  return h('VView', { style: { padding: 16 } }, [
    h('VText', {}, `${item.title}`),
  ])
}
</script>

<template>
  <VFlatList
    :data="data"
    :renderItem="renderItem"
    :itemHeight="52"
    :style="{ flex: 1 }"
  />
</template>

Typing

VFlatList is a generic component: renderItem and keyExtractor infer the item type T from the data prop, so you get full type-checking and autocompletion on item without manual annotations.

<script setup lang="ts">
import { h } from '@thelacanians/vue-native-runtime'

interface User {
  id: number
  name: string
  email: string
}

const data: User[] = [
  { id: 1, name: 'Ada', email: 'ada@example.com' },
  { id: 2, name: 'Linus', email: 'linus@example.com' },
]

// `item` is inferred as `User` — `item.name` type-checks, `item.unknown` errors.
function renderItem({ item, index }: { item: User, index: number }) {
  return h('VView', { style: { padding: 16 } }, [
    h('VText', {}, `${item.name} <${item.email}>`),
  ])
}

// `item` is inferred as `User` here too.
const keyExtractor = (item: User) => item.id
</script>

<template>
  <VFlatList
    :data="data"
    :renderItem="renderItem"
    :keyExtractor="keyExtractor"
    :itemHeight="52"
    :style="{ flex: 1 }"
  />
</template>

The same inference applies to the #item slot in <script setup lang="ts"> templates — the slot's item is typed from data.

VList vs VFlatList

VListVFlatList
BackingNative UITableView / RecyclerViewJS-side virtualization over VScrollView
Item heightVariable (estimated via estimatedItemHeight)Fixed (required itemHeight prop)
Best forSmall-to-medium lists, variable row heightsVery large datasets (1000+ items), fixed row heights
MemoryNative cell reuseOnly visible + buffer items mounted

Performance

Set itemHeight to the exact pixel height of your rows. VFlatList uses this value for all scroll position math -- an incorrect value will cause items to appear at wrong positions.

windowSize

The default windowSize of 3 means 3 viewports above + 3 viewports below the visible area are rendered (7 total viewports of items). Increase this if you see blank flashes during fast scrolling, or decrease it to save memory.

Edit this page
Last Updated: 8/12/26, 5:15 AM
Contributors: github-actions[bot], Abdul Hamid, Claude Fable 5
Prev
VList
Next
VSectionList