ScrollView
A SwiftUI ScrollView component for scrollable content.
Expo UI ScrollView matches the official SwiftUI ScrollView API and provides a scrollable container for its children.
Usage
Basic vertical scroll view
A simple vertically scrollable list of text items.
import { Host, ScrollView, VStack, Text } from '@expo/ui/swift-ui';
import { padding } from '@expo/ui/swift-ui/modifiers';
export default function ScrollViewVerticalExample() {
return (
<Host style={{ flex: 1 }}>
<ScrollView>
<VStack spacing={8}>
{Array.from({ length: 30 }, (_, i) => (
<Text key={i} modifiers={[padding({ horizontal: 16 })]}>
{`Item ${i + 1}`}
</Text>
))}
</VStack>
</ScrollView>
</Host>
);
}Horizontal scroll view
Use the axes prop to scroll horizontally.
import {
Host,
ScrollView,
HStack,
RoundedRectangle,
} from '@expo/ui/swift-ui';
import {
frame,
foregroundStyle,
} from '@expo/ui/swift-ui/modifiers';
export default function ScrollViewHorizontalExample() {
return (
<Host style={{ flex: 1 }}>
<ScrollView axes="horizontal">
<HStack spacing={8}>
{Array.from({ length: 20 }, (_, i) => (
<RoundedRectangle
key={i}
cornerRadius={12}
modifiers={[
frame({ width: 100, height: 100 }),
foregroundStyle(`hsl(${i * 18}, 70%, 50%)`),
]}
/>
))}
</HStack>
</ScrollView>
</Host>
);
}Hidden scroll indicators
Set showsIndicators to false to hide the scroll bars.
import { Host, ScrollView, VStack, Text } from '@expo/ui/swift-ui';
export default function ScrollViewHiddenIndicatorsExample() {
return (
<Host style={{ flex: 1 }}>
<ScrollView showsIndicators={false}>
<VStack spacing={8}>
{Array.from({ length: 30 }, (_, i) => (
<Text key={i}>{`Item ${i + 1}`}</Text>
))}
</VStack>
</ScrollView>
</Host>
);
}Shared scroll position
Requires iOS 17 or later. On older versions, the modifier is a no-op.
Track the leading scroll target id from JavaScript and scroll to a target by writing to the state. Mark each scroll target with the id modifier, wrap the content container in scrollTargetLayout, and apply the scrollPosition modifier to the ScrollView. The optional onChange callback fires on the JS thread when the leading target changes.
The scrollPosition modifier also works on other scrollable containers like LazyVStack and LazyHStack.
Writes to
state.valuemust run on the UI runtime. Wrap the write inscheduleOnUIfromreact-native-worklets, or call them from inside a'worklet'function. Writes from the JS runtime trip Main Thread Checker, Xcode's runtime tool that flags UIKit calls made from a background thread.
import {
Button,
Host,
ScrollView,
Text,
VStack,
useNativeState,
} from '@expo/ui/swift-ui';
import {
id,
padding,
scrollPosition,
scrollTargetLayout,
} from '@expo/ui/swift-ui/modifiers';
import { scheduleOnUI } from 'react-native-worklets';
export default function ScrollViewSharedPositionExample() {
const activeID = useNativeState<string | null>(null);
return (
<Host style={{ flex: 1 }}>
<VStack spacing={12}>
<ScrollView
modifiers={[
scrollPosition(activeID, {
onChange: newID => {
console.log('[JS thread] leading target:', newID);
},
}),
]}>
<VStack modifiers={[scrollTargetLayout()]}>
{Array.from({ length: 30 }, (_, i) => (
<Text
key={`item-${i}`}
modifiers={[
id(`item-${i}`),
padding({ horizontal: 16, vertical: 12 }),
]}>
{`Item ${i}`}
</Text>
))}
</VStack>
</ScrollView>
<Button
label="Scroll to item 10 from worklet"
onPress={() => {
scheduleOnUI(() => {
'worklet';
activeID.value = 'item-10';
});
}}
/>
</VStack>
</Host>
);
}API
import { ScrollView } from '@expo/ui/swift-ui';Component
ScrollView
Type: React.Element<ScrollViewProps>
SwiftUI ScrollView wrapper. To control scroll position, pair this with the scrollPosition(state, { onChange }) modifier and a useNativeState-backed id. Write state.value = targetId for an instant scroll, or wrap the write in withAnimation(...) from @expo/ui/swift-ui for an animated one.
ScrollViewProps
axes
Optional • Literal type: string • Default: 'vertical'
The scrollable axes. Pass 'both' to enable 2D (horizontal + vertical) scrolling.
Acceptable values are: 'vertical' | 'horizontal' | 'both'
children
Type: ReactNode
showsIndicators
Optional • Type: boolean • Default: true
Whether to show scroll indicators. For richer visibility control (e.g. 'never') or per-axis control, use the scrollIndicators(...) modifier instead.
Inherited props
Types
ScrollGeometry
Snapshot of a ScrollView's scroll geometry, emitted by the useScrollGeometryChange(...) and onScrollPhaseChange(...) modifiers (iOS 18+).
| Property | Type | Description |
|---|---|---|
| containerHeight | number | Height of the visible scroll container, in points. |
| containerWidth | number | Width of the visible scroll container, in points. |
| contentHeight | number | Total height of the scrollable content, in points. |
| contentOffsetX | number | Horizontal content offset, in points. |
| contentOffsetY | number | Vertical content offset, in points. |
| contentWidth | number | Total width of the scrollable content, in points. |
ScrollPhase
Literal type: string
Scroll phase emitted by the onScrollPhaseChange(...) modifier. Mirrors SwiftUI's ScrollPhase (iOS 18+).
Acceptable values are: 'idle' | 'tracking' | 'interacting' | 'animating' | 'decelerating'
Modifiers
Use these modifiers in the modifiers prop of the SwiftUI and Universal version.
Modifiers for ScrollView
defaultScrollAnchoriOS 17+defaultScrollAnchorForRoleiOS 18+onScrollPhaseChangeiOS 18+refreshablescrollDisablediOS 16+scrollDismissesKeyboardiOS 16+scrollIndicatorsiOS 16+scrollPositioniOS 17+scrollTargetBehavioriOS 17+
Modifiers for any view
accessibilityAddTraitsaccessibilityElementaccessibilityHiddenaccessibilityHintaccessibilityIdentifieraccessibilityInputLabelsaccessibilityLabelaccessibilityRemoveTraitsaccessibilityValueactivityBackgroundTintalignmentGuideallowsTighteninganimationaspectRatiobackgroundbackgroundOverlayblurboldborderbrightnessclippedclipShapecolorInvertcontainerBackgroundcontainerRelativeFrameiOS 17+containerShapecontentShapecontentTransitioniOS 16+contrastcornerRadiuscreateModifierdisableddynamicTypeSizeenvironmentfixedSizefontforegroundColorforegroundStyleframegeometryGroupiOS 17+glassEffectglassEffectIdgrayscalegridCellAnchoriOS 16+gridCellColumnsgridCellUnsizedAxesgridColumnAlignmentiOS 16+hiddenhueRotationidignoreSafeAreaimageScaleinvalidatableContentiOS 17+italickerninglayoutPrioritylineHeightiOS 26+lineLimitlineSpacingluminanceToAlphamaskmatchedGeometryEffectminimumScaleFactormonospacedDigitmultilineTextAlignmentoffsetonAppearonDisappearonGeometryChangeonLongPressGestureonTapGestureopacityoverlaypaddingprivacySensitiveredactedrotation3DEffectrotationEffectsaturationscaleEffectshadowstrikethroughstrokeBordertextCasetinttruncationModeunderlineunredactedwidgetURLzIndex
Sources
The original Expo docs for this page:
- ScrollView (SwiftUI) (
source-of-truth/versions/v57.0.0/sdk/ui/swift-ui/scrollview.md) - ScrollView (Universal) (
source-of-truth/versions/v57.0.0/sdk/ui/universal/scrollview.md)