API reference
Exports of @sigx/lynx-list v0.27.0.
Exports
| Export | Kind | Purpose |
|---|---|---|
List | component | The virtualized list. |
ListMethods | object | Imperative scroll methods for a captured element. |
ListProps<T> | type | Props of List, generic over the item type. |
ListRef | type | The mtRef type — a main-thread ref to the native element. |
ListType | type | 'single' | 'flow' | 'waterfall'. |
ListItemSnap | type | 'start' | 'center' | 'end' | { factor?: number; offset?: number; maxSnapCount?: number } — see itemSnap. |
ScrollAlign | type | 'top' | 'bottom' | 'middle'. |
ScrollToIndexOptions | type | Options for ListMethods.scrollToIndex. |
import { List, ListMethods } from '@sigx/lynx-list';
import type { ListProps, ListRef } from '@sigx/lynx-list';
<List> props
items and renderItem are the only required props; both are generic over the
element type T of items.
| Prop | Type | Notes |
|---|---|---|
items | readonly T[] | Required. The data. |
renderItem | (item: T, index: number) => JSX | Required. Per-cell renderer. |
keyExtractor | (item: T, index: number) => string | Stable recycler key (item-key). Defaults to the index — set it for lists that mutate. |
itemType | (item: T, index: number) => string | Recycle-pool selector (item-type) for mixed cell shapes. |
estimatedItemSize | number | Main-axis px estimate for uniform rows; improves scroll accuracy. Omit for variable-height content (chat) — a too-small estimate clips items until measured. |
initialMainAxisSize | number | Pins the native list's main-axis px on the first frame instead of the 1px placeholder; the live measure still wins once it lands. Use it when you know the height up front (a keyboard panel, a fixed grid). |
horizontal | boolean | Horizontal scrolling. |
numColumns | number | Grid columns (span-count). |
listType | ListType | Layout mode: single (default), flow, waterfall. |
itemSnap | ListItemSnap | Paginated snap: the alignment shorthand 'start' / 'center' / 'end', or { factor, offset, maxSnapCount }. Omit the prop to disable snapping. See below. |
onEndReachedThreshold | number | Items-from-end at which onEndReached fires. |
onStartReachedThreshold | number | Items-from-start at which onStartReached fires. |
loadingMore | boolean | Show a trailing loading cell (infinite scroll). |
refreshing | boolean | Controlled pull-to-refresh state; passing it opts in (vertical only). |
pullThreshold | number | Pull distance (px) that triggers a refresh. Default 64. |
inverted | boolean | Chat mode: bottom-anchored + stick-to-bottom (vertical only). |
stickToBottom | boolean | In chat mode, auto-scroll on new items when at the bottom. Default true. While at the bottom the pin is re-applied on every layoutcomplete, so late cell growth cannot strand the viewport. |
newMessagesOffset | number | Raises the chat newMessages affordance off the wrapper's bottom edge. Default 12 — enough to clear nothing, so set it to clear a floating composer. |
bottomInset | number | SharedValue<number> | Bottom content inset the recycler keeps clear, applied natively with no CSS write and no layout pass — so a SharedValue can drive it per frame. See below. |
windowSize | number | Enables windowing: render only this many cells of a long items. |
pageSize | number | Items revealed per scroll-edge page when windowing. Default 30. |
maxWindow | number | Cap on rendered window length; the far end trims past it. Default max(120, windowSize × 2). |
itemsKey | string | Dataset identity. When it changes, items is treated as a brand-new list: the window re-anchors to its initial position and scroll resets to the start (bottom in chat mode). Omit it for append / prepend / edit of the same logical list. |
mtRef | ListRef | Capture the native element for ListMethods. |
class / style | — | Applied to the measuring wrapper <view>. |
Events
| Event | Payload | Fires |
|---|---|---|
onEndReached | — | Within onEndReachedThreshold items of the end. |
onStartReached | — | Within onStartReachedThreshold items of the start. |
onScroll | { offset: number } | On scroll, with the current offset. |
onRefresh | — | On a pull-to-refresh past pullThreshold. |
Slots
Passed through the slots prop.
| Slot | Signature | Renders |
|---|---|---|
header | () => JSX | A full-span cell before the items. |
footer | () => JSX | A full-span cell after the items. |
empty | () => JSX | In place of the list when items is empty. |
refresh | () => JSX | Custom pull-to-refresh indicator. |
newMessages | ({ count }) => JSX | Chat-mode "new messages" affordance while scrolled up. |
ListMethods
Typed wrappers around the native scroll UI methods, callable from any main-thread
event handler. Both accept el | null, so you can pass ref.current directly; a
null element is a silent no-op.
ListMethods.scrollToTop(el); // jump to top
ListMethods.scrollToTop(el, { smooth: true }); // animated
ListMethods.scrollToIndex(el, 12, { align: 'middle', smooth: true });
scrollToTop(el, opts?)— scroll to the first cell (the top of the list, header included).scrollToIndex(el, index, opts?)— scroll so the cell atindexaligns peropts.align.indexis the rendered cell index, not the data index — add 1 to a data index when aheaderslot is present.
ScrollToIndexOptions:
| Field | Type | Default | Notes |
|---|---|---|---|
align | ScrollAlign | 'top' | Align the target cell to top, bottom, or middle. |
offset | number | 0 | Extra pixel offset applied after alignment. |
smooth | boolean | false | Animate the scroll (default is a jump). |
scrollHandle
ListMethods needs the native element. scrollHandle is the prop form — pass an
identity-stable handle and the List populates it at setup:
// Module or component scope — NOT inside render. The handle must keep its identity.
const handle: ListScrollHandle = {};
<List scrollHandle={handle} … />
handle.scrollToEnd?.(); // jump to the newest
handle.scrollToEnd?.({ smooth: true });
scrollToEnd is the one piece of index math you cannot do yourself once windowing
is on: it accounts for the header, footer and loading cells and the rendered
window. In chat mode it also marks the viewport at-bottom and clears the unread
affordance, so a target cell that has not reached native yet is caught by the next
relayout's re-pin.
The handle must be identity-stable — build it once, outside render.
itemSnap
Snaps cells into place when a paginated scroll settles — the native item-snap contract, which both platforms read as a dictionary.
type ListItemSnap = 'start' | 'center' | 'end' | {
/** Where the item settles, as a fraction of the viewport: 0 start, 0.5 centre, 1 end. Default 0. */
factor?: number;
/** Pixel offset from the alignment line. Default 0. */
offset?: number;
/** How many items a single fling may cross (Lynx 4.0+). Default 1. */
maxSnapCount?: number;
};
The shorthand maps to factor 0 / 0.5 / 1. A factor outside [0, 1] is corrected to 0 and a maxSnapCount below 1 to 1 — what native does — with a dev-build warning. maxSnapCount is a bound, not a forced distance: a slow fling still moves one page; a fast one may page up to that many. Hosts older than Lynx 4.0 ignore it. To disable snapping, omit the prop — an absent item-snap is what native means by "no paging".
<List horizontal itemSnap="center" … />
<List horizontal itemSnap={{ factor: 0, offset: 16, maxSnapCount: 3 }} … />
Sticky section headers (sticky-top / sticky-bottom) use the engine's sticky layout; pluginSigxLynx({ enableNewSticky: true }) opts into Lynx 4.0's new implementation, which changes where and when a header pins — see the plugin's page-config flags.
bottomInset
The recycler keeps bottomInset pixels of its bottom viewport clear. It is applied
natively, as a padding/inset delta rather than a CSS write, so there is no
layout pass and a main-thread SharedValue — useKeyboardLiftSV(), a
BottomSheet's onReveal — can drive it every frame:
const lift = useKeyboardLiftSV();
<List inverted bottomInset={lift} … />
In chat mode the newest item stays anchored above the inset via same-frame native
compensation, and at-bottom detection becomes inset-aware — so a compensation
scroll cannot falsely release the pin or fire a spurious endReached. The native
compensation is gated on the list being pinned at the bottom, which is why the
first paint of a chat thread lands on the newest message on both platforms: the
pin is established before the inset is applied. No paddingBottom workaround is
needed on either platform.
This is the package's first native side. Run sigx prebuild after
upgrading. Hosts without the native module — the web target — ignore the inset
rather than failing.
See Usage for worked examples of each feature.
