# Migration to v3

Migration to v3 — Legend List documentation.

Source: https://legend.so/list/v3/migration/

Legend List v3 documentation. Current documentation version.

Version 3 introduces first‑class Web support and a new SectionList component, plus several API improvements.

## [Migrating from 3.6 to 3.7](https://legend.so/list/v3/migration/#migrating-from-36-to-37)

**New in 3.7** (3.7.0 is unreleased).

The only breaking type changes are new required members on the public ref and state types: code that implements or mocks these types must add them. Apps that only consume refs and state do not need to implement these members themselves. There are no deprecations in 3.7.

| Type | New required members |
| --- | --- |
| `LegendListRef` (React Native and Web) | `cancelScroll`, `scrollToKey`, `anchorVisibleContent` |
| `SectionListRef` | `cancelScroll`, `scrollToKey`, `anchorVisibleContent`, inherited from `LegendListRef` |
| `LegendListState` | `distanceFromEnd`, `isFollowingEnd` |

`LegendListState.dataKey` is a new optional member. `listen` accepts `isFollowingEnd`; `distanceFromEnd` is computed when `getState()` is called and is not a listen key. Existing 3.6-shaped `listen` implementations remain assignable. `LegendListMetrics` is unchanged: `onMetricsChange` still receives only `headerSize` and `footerSize`.

For example, update a test double that only needs to model cancellation and unreachable key navigation with these members. A mock that simulates actual scrolling should model the new completion behavior too.

```ts
import type { LegendListRef, LegendListState } from "@legendapp/list/react-native";

const newRefMembers: Pick<LegendListRef,
  "cancelScroll" | "scrollToKey" | "anchorVisibleContent"
> = {
  cancelScroll: () => {},
  scrollToKey: async () => {},
  anchorVisibleContent: () => undefined,
};

const newStateMembers: Pick<LegendListState,
  "distanceFromEnd" | "isFollowingEnd"
> = {
  distanceFromEnd: 0,
  isFollowingEnd: false,
};
```

Add those members to your existing mock's ref and state objects. See the [ref methods](https://legend.so/list/v3/api#ref-methods) and [state fields](https://legend.so/list/v3/api#fields-and-methods) for their contracts.

For SectionList key-based methods, pass flattened item keys of the form `${sectionKey}:item:${itemKey}`, rather than the bare item key. `sectionKey` is the section's `key` or `section-${sectionIndex}` when omitted; `itemKey` comes from the section or list `keyExtractor`; without one, it uses `item.key`, then `item.id`, then the item index within the section, converted to a string. `anchorVisibleContent` accepts and returns those same flattened keys. See [SectionList](https://legend.so/list/v3/api#sectionlist) for header, footer, and separator keys.

Behavior also changes in 3.7:

-   Scroll promises now resolve on landing or settlement, including interruption, cancellation, replacement, or an unreachable target, instead of resolving early or never. Resolution does not guarantee arrival.
-   Use `maintainScrollAtEnd.on.dataAppend` with a stable `keyExtractor` for append-only following, leaving `dataChange` disabled. Without `keyExtractor`, any increase in data length, including a prepend, triggers following.
-   With a trailing inset, `isAtEnd`, `onEndReached`, and `maintainScrollAtEnd` now activate closer to the physical end because the inset is counted once.
-   Allow `onStartReached` / `onEndReached` to run after layout changes without a scroll event.
-   Pass external fixed-size dependencies through `extraData` so `getFixedItemSize` sizes are invalidated when they change.

See [Chat interfaces](https://legend.so/list/v3/guides#chat-interfaces) and [Scrolling a search match into view](https://legend.so/list/v3/guides#scrolling-a-search-match-into-view) for examples.

## [✨ New in v3](https://legend.so/list/v3/migration/#-new-in-v3)

### [Web Support](https://legend.so/list/v3/migration/#web-support)

-   DOM‑native rendering (no React Native dependency required)
-   Same virtualization core as React Native
-   Web examples and docs

### [SectionList Component](https://legend.so/list/v3/migration/#sectionlist-component)

-   `@legendapp/list/section-list` with a React Native‑compatible API
-   `scrollToLocation` support
-   Sticky section headers powered by `stickyHeaderIndices`

### [Always Render](https://legend.so/list/v3/migration/#always-render)

-   `alwaysRender` keeps selected items mounted outside the virtualization window

### [Better Scroll & Metrics APIs](https://legend.so/list/v3/migration/#better-scroll--metrics-apis)

-   `initialScrollAtEnd` for chat and feeds
-   `anchoredEndSpace` for reserving readable space after a chat or feed anchor row
-   `contentInsetEndAdjustment` for floating composers and overlay UI at the end of a list
-   `useKeyboardChatComposerInset` for React Native keyboard chat composers
-   `onMetricsChange` for header/footer size changes
-   `getState()` now exposes listeners, element access, and scroll velocity

## [🔄 Breaking changes from v2](https://legend.so/list/v3/migration/#-breaking-changes-from-v2)

1.  **Typed import paths**
    
    -   The root `@legendapp/list` component export has been removed in v3.
    -   Use:
        -   React Native: `@legendapp/list/react-native`
        -   React (Web): `@legendapp/list/react`
2.  **`maintainVisibleContentPosition` defaults**
    
    -   v3 now enables scroll-position stabilization on size changes by default
    -   Default behavior is equivalent to `maintainVisibleContentPosition={{ size: true, data: false }}`
    -   Toggle specific behavior as needed:
        -   `maintainVisibleContentPosition={{ data: true }}` to anchor on data changes too
        -   `maintainVisibleContentPosition={{ size: false }}` to disable size/layout stabilization
        -   `maintainVisibleContentPosition={true}` to enable both `size` and `data`
        -   `maintainVisibleContentPosition={false}` to disable both
3.  **Size hints and fixed-size callbacks**
    
    -   `getFixedItemSize` is now `(item, index, type)`
    -   `getEstimatedItemSize` has been removed. Prefer no estimate for most dynamic-size lists, `estimatedItemSize` only as a small initial allocation hint when rows are far from `100px`, or `getFixedItemSize` when sizes are known exactly.
    -   `suggestEstimatedItemSize` has been removed. Use `ref.current?.getState().getAverageItemSizes()` to inspect measured average sizes directly.
4.  **Sticky headers prop rename**
    
    -   `stickyIndices` has been removed. Use `stickyHeaderIndices`.
5.  **Keyboard integration entrypoint update**
    
    -   v2 keyboard docs used `@legendapp/list/keyboard-controller` and `LegendList`.
    -   In v3 use `@legendapp/list/keyboard` and import `KeyboardAwareLegendList`.
    -   `LegendList` is no longer exported from the `@legendapp/list/keyboard` entrypoint.
    -   Chat screens can use `KeyboardAwareLegendList`, `useKeyboardScrollToEnd`, and `useKeyboardChatComposerInset` from `@legendapp/list/keyboard`.
    -   `KeyboardAvoidingLegendList` is available from `@legendapp/list/keyboard-legacy` for apps that still need the previous keyboard-avoiding integration.
6.  **`renderItem` is a render callback**
    
    -   `renderItem` is called directly as `(props) => ReactNode`.
    -   Do not pass a hook-using item component directly as `renderItem={Row}`.
    -   Return hook-using item components from the callback instead: `renderItem={({ item }) => <Row item={item} />}`.
7.  **Imperative scroll methods are async**
    
    -   These ref methods now return `Promise<void>`:
        -   `scrollIndexIntoView`
        -   `scrollItemIntoView`
        -   `scrollToEnd`
        -   `scrollToIndex`
        -   `scrollToItem`
        -   `scrollToOffset`
    -   If you run logic after a programmatic scroll, `await` the call. The promise also resolves if the scroll is cancelled or superseded, so resolution does not guarantee arrival at the target.
8.  **`getState()` shape changed for advanced integrations**
    
    -   `state.positions` is no longer part of the public `getState()` return value.
    -   Replace direct map access with:
        -   `positionAtIndex(index)`
        -   `positionByKey(key)`
    -   `getState()` now also exposes listener helpers (`listen`, `listenToPosition`), `scrollVelocity`, and `getAverageItemSizes()`.
9.  **Removed compatibility props and types**
    
    -   `initialContainerPoolRatio` has been removed. LegendList manages spare container capacity automatically.
    -   `InitialScrollAnchor` has been removed. Use `ScrollIndexWithOffsetPosition` for public initial-scroll position typing.

## [Migration checklist](https://legend.so/list/v3/migration/#migration-checklist)

-   Move imports to typed platform entrypoints (`/react-native` or `/react`)
-   Update fixed-size callback signatures to `(item, index, type)`
-   Replace `getEstimatedItemSize` with no estimate, `estimatedItemSize` only for unusual initial sizes, or `getFixedItemSize`
-   Replace `suggestEstimatedItemSize` with `getState().getAverageItemSizes()`
-   Replace `stickyIndices` with `stickyHeaderIndices`
-   Remove `initialContainerPoolRatio`; spare container capacity is automatic
-   Replace `InitialScrollAnchor` type references with `ScrollIndexWithOffsetPosition`
-   Update keyboard imports from `@legendapp/list/keyboard-controller` to `@legendapp/list/keyboard` and use `KeyboardAwareLegendList`
-   If you still need the previous keyboard-avoiding integration, import `KeyboardAvoidingLegendList` from `@legendapp/list/keyboard-legacy`
-   Wrap hook-using item components in a `renderItem` callback instead of passing them directly
-   For floating composers, replace content padding workarounds with `contentInsetEndAdjustment` on web or `useKeyboardChatComposerInset` with `KeyboardAwareLegendList` on React Native
-   `await` imperative scroll calls to wait for completion or cancellation
-   Update advanced `getState()` consumers to use `positionAtIndex` / `positionByKey` instead of `positions`

## [Install](https://legend.so/list/v3/migration/#install)

```bash
npm install @legendapp/list
```

[View Markdown](https://legend.so/list/v3/migration.md)[Report a documentation correction](https://github.com/LegendApp/legend-docs/issues/new?title=Docs%3A+Migration+to+v3&body=Page%3A+https%3A%2F%2Flegend.so%2Flist%2Fv3%2Fmigration%0A%0AWhat+is+incorrect+or+missing%3F%0A%0AExpected+behavior+or+supporting+source%3A%0A)
