Concepts
v5 separates four ideas that older APIs often mixed together: viewport size, page size, visual position, and settled selection.
Viewport and page size
The root style defines the viewport. Its measured main-axis size is also the default page distance:
<Carousel
style={{ width: 360, height: 200 }}
data={data}
renderItem={renderItem}
/>Set itemSize only when the page distance differs from the viewport:
<Carousel
style={{ width: 360, height: 200 }}
itemSize={240}
data={data}
renderItem={renderItem}
/>The same itemSize prop applies to both orientations. In horizontal mode it is a width; in vertical mode it is a height.
Raw indices
Every public index refers to the original data array:
renderItem.index;defaultIndex;scrollTo({ index });getCurrentIndex();onSnapToItem(index);- Pagination press indices.
Loop mode may create internal copies for short arrays, but copy indices never escape the component.
Use keyExtractor when identity must survive insertion or reordering. If the selected key disappears, the old numeric index is clamped.
Logical progress
progress is a fractional logical index:
backward ← -1 0 1 2 → forward- item
0is progress0; - forward movement adds
1per item; - backward movement subtracts
1; - fractions represent in-flight movement;
- non-loop progress is bounded;
- loop progress is continuous and unbounded.
For loop progress, the settled raw index is the positive modulo of rounded progress by data.length. Pagination applies the same nearest-cycle model, so it does not need a loop prop or a seam special case.
Progress continuity is guaranteed while data length is stable. A data-length change can rebase the coordinate once without emitting lifecycle events.
Signed offset
scrollOffsetValue exposes the main-axis content translation in pixels:
item 0 item 1 item 2
offset 0 → -itemSize → -2 * itemSizeThe sign is invariant across horizontal, vertical, LTR, and RTL. Loop offset is continuous and unbounded.
This SharedValue is two-way: an external write moves content. It does not commit selection or fire onSnapToItem; use scrollTo({ index }) to finish at a semantic item. During an active gesture, gesture frames own the offset and may overwrite external writes.
Relative item progress
Each renderItem receives relativeProgress:
backward item selected item forward item
-1 0 +1It is a SharedValue<number> for animated styles. itemAnimation receives the same value as a number on the UI thread.
The coordinate is logical. A custom horizontal translateX, however, is a physical React Native transform and must be mirrored by the application for RTL when the effect is directional.
Live position and settled selection
Visual position changes continuously, while public selection changes only on settle:
progressandscrollOffsetValueupdate during motion;getCurrentIndex()stays at the previous settled index;onSnapToItemannounces the new settled raw index.
This prevents application state from oscillating between rounded indices during a swipe.
onScrollStart marks the start of an accepted movement from gestures, commands, or autoplay. Rejected/no-op commands, direct offset writes, relayout, and data reconciliation do not emit lifecycle events.
Looping
loop defaults to false. With loop:
nextandprevpreserve their requested logical direction;scrollTotakes the shortest path and chooses logical forward on a tie;- progress and offset remain unbounded rather than resetting at cycle boundaries;
- built-in rendering uses bounded derived transforms to avoid precision problems in native view styles.
Autoplay follows the same logical raw-data order. In non-loop mode it stops at the requested direction's boundary.
Snapping
snapMode describes release behavior:
"page": move at most one logical page per gesture;"nearest": settle on the nearest logical page after free movement;"none": use free decay, then publish the nearest raw index when motion stops.
animation controls page/nearest settle, commands, and autoplay. Free decay and overscroll rebound remain internal physics.
Render window
renderWindowSize limits how many slides are mounted around the current position. Omit it to mount all raw slides.
This is a rendering optimization only. It does not change data, indices, progress, looping, or navigation.
RTL
Horizontal RTL is detected from I18nManager.isRTL. Public APIs stay logical:
- data order is unchanged;
nextis still forward;- progress still increases forward;
- offset still decreases forward;
- a physical right swipe advances in RTL.
Only physical presentation and gesture coordinates are mapped. Vertical carousels are unaffected. Gesture observers receive the original physical RNGH event values.
Accessibility
Only the current slide is exposed to the accessibility tree. The application owns slide semantics through renderItem.
Interactive Pagination dots are buttons with selected state. Without onPress, dots are decorative and hidden. External previous/next buttons are a good complement when screen-reader users should move through slides without swiping.
Adding an example
Use itemAnimation for effects not covered by the built-in layouts.
- Add a kebab-case demo route under
example/app/app/demos/<category>/<name>by following a nearby example. - When it should appear in the documentation, add a self-contained
demo.tsxandpreview.pngin that route. - Run
yarn --cwd example/website gen-pages; do not edit generated files underexample/website/pages/Examplesdirectly. - Run
yarn --cwd example/website buildto validate the generated page. - Run
yarn giffrom the repository root when a GIF asset is needed. - Put generated media under
assetsand reference it from the example source.