Skip to main content

ScrollView

<SkiaScrollView />

Use <SkiaScrollView /> as a replacement for the React Native <ScrollView /> component.

info

It uses the Skia rendering engine to render the content so you can't use React Native components inside it.

note

You must specify the height prop of ScrollGestureProps to make the list scrollable.

Example

const paint = Skia.Paint();
paint.setColor(Skia.Color("rgb(91, 128, 218)"));

const circleCount = 100;

<SkiaScrollView height={circleCount * 100} style={{ flex: 1 }}>
{Array.from({ length: circleCount }, (_, i) => (
<Circle key={i} cx={50} cy={50 + i * 100} r={50} paint={paint} />
))}
</SkiaScrollView>

Example with useSkiaScrollView

You can manage the list state yourself by using useSkiaScrollView:
This is useful when you need to build custom behavior on top of the list, e.g. custom gestures/renderer.

const state = useSkiaScrollView({ height: 1000 });
const content = content.value;

content.addChild(SkiaDomApi.RectNode({ width: 100, height: 100, x: 0, y: 0 }));

<SkiaScrollView list={state} style={{ flex: 1 }} />

SkiaScrollViewElementProps

list?: SkiaScrollViewState

Manage the list state yourself using useSkiaScrollView:

const list = useSkiaScrollView({ height: 1000 });

// do something with the list, e.g. skew the list content:
list.matrix.value[1] = 0.1;

<SkiaScrollView list={list} />

style?: ViewStyle

The view style of the canvas style. You should specify flex: 1 to ensure the canvas fills the screen.

<SkiaScrollView style={{ flex: 1 }} />

children?: ReactNode

Use children to render skia elements.

info

You must manually specify the height prop of ScrollGestureProps to make the list scrollable.

<SkiaScrollView height={100} style={{ flex: 1 }}>
<Circle cx={50} cy={50 + 100} r={50} color="blue" />
</SkiaScrollView>
info

If you are updating the children very fequently, you should consider using useSkiaScrollView to improve performance by imperatively updating the list content and therefore avoiding the React reconciler.

const state = useSkiaScrollView({ height: 1000 });
const content = content.value;

let previous = null;
// do something with the list content, e.g. add a new rect every 10ms
setInterval(() => {
if (previous) content.removeChild(previous);

previous = SkiaDomApi.RectNode({
width: 100, height: 100,
x: Math.random() * 1000,
y: Math.random() * 1000
});

content.addChild(previous);
}, 10);

fixedChildren?: ReactNode

Use fixedChildren to render skia elements that are displayed fixed on top of the list content and are not scrollable.

<SkiaScrollView fixedChildren={<Fill color="blue" />} />

debug?: boolean

Enable debug mode to show the FPS count and render time.

keyboardDismissMode?: "none" | "interactive" | "on-drag"

Determines whether the keyboard gets dismissed in response to a drag.

  • none (the default) drags do not dismiss the keyboard.
  • onDrag the keyboard is dismissed when a drag begins.
  • interactive the keyboard is dismissed interactively with the drag and moves in synchrony with the touch; dragging upwards cancels the dismissal.

keyboardShouldPersistTaps?: boolean | "always" | "never"

Determines when the keyboard should stay visible after a tap.

  • never (the default), tapping outside of the focused text input when the keyboard is up dismisses the keyboard. When this happens, children won`t receive the tap.
  • always, the keyboard will not dismiss automatically, and the scroll view will not catch taps, but children of the scroll view can catch taps.
  • false, deprecated, use never instead
  • true, deprecated, use always instead

ScrollGestureInitalState

layout: SharedValue<object>

Contains the width and height of the scroll view. Will automatically update when the layout changes.

width: number

height: number

scrollY: SharedValue<number>

The current scroll Y position.

startY: SharedValue<number>

Used internally to keep track of the start Y position when dragging.

offsetY: SharedValue<number>

Used to indepentently control the scroll position without affecting the scrollY value (which is used by the gesture handler) e.g. used for keyboard handling.

maxHeight: SharedValue<number>

The maximum height the scroll view can scroll to. Automatically updated when the layout/data changes.

scrolling: SharedValue<boolean>

Shared value that indicates if the view is currently being scrolled.

scrollingDisabled: SharedValue<boolean>

Shared value to disable scrolling.

bounces: boolean

If the scroll view should bounce when reaching the top or bottom.

decelerationRate: number

The deceleration rate for momentum scrolling. Default is 0.998.


ScrollGestureProps

height?: number

Sets the initial value for maxHeight while taking the layout height into consideration.

warning

You need to set this value for SkiaScrollView to be able to scroll.

inverted?: boolean

Inverts the scroll direction of the Gesture Handler. Used in conjunction with inverted of SkiaScrollViewProps.

onScroll?: (value) => void

Callback that is invoked every time the scroll position changes. Needs to be a worklet function. You need to implement throttling yourself:

function onScroll(event: ScrollEvent) {
"worklet";
const { scrollY } = event;
// do something with scrollY, e.g. update the position of a sticky header
}

<SkiaScrollView onScroll={onScroll} />

onScrollBeginDrag?: () => void

Callback that is invoked when the user starts dragging the scroll view. Needs to be a worklet function.

onScrollEndDrag?: () => void

Callback that is invoked when the user stops dragging the scroll view. Needs to be a worklet function.

onMomentumScrollEnd?: () => void

Callback that is invoked when the momentum scroll begins. Needs to be a worklet function.

onMomentumScrollBegin?: () => void

Callback that is invoked when the momentum scroll ends. Needs to be a worklet function.

startedAnimation?: () => void

Call this function when you start animating an SharedValue to enable continous rendering mode.

finishedAnimation?: () => void

Call this function when you finish animating an SharedValue to return to default rendering mode.

warning

Ensure that you have the equal amount of startedAnimation and finishedAnimation calls to avoid unecessary re-renders when idling.


ScrollGestureState

gesture: GestureType

scrollTo: (opts) => void

Scroll to a certain Y position.

scrollToEnd: (opts) => void

Scroll to the end of the scroll view (inferred by maxHeight).

scrollToStart: (opts) => void

Scroll to start of the scroll view.

startMomentumScroll: (velocityY) => void

If you manually handle scrolling, e.g. with a custom ScrollBar, you can call this function to start momentum scrolling.


useSkiaScrollView(SkiaScrollViewProps): SkiaScrollViewState

Use this hook to manage and access the state of SkiaScrollView.

const state = useSkiaScrollView({ height: 1000 });

state.content.value.addChild(SkiaDomApi.RectNode({ width: 100, height: 100, x: 0, y: 0 }));

<SkiaScrollView list={state} style={{ flex: 1 }} />

InitialScrollViewState

root: SharedValue<RenderNode<GroupProps>>

The root group node of the skia list view with a fixed position that contains the content group node

You can transform the entire list by setting the matrix property.

root.value.setProp("matrix", Skia.Matrix().skew(1, 0.5).get());

content: SharedValue<RenderNode<GroupProps>>

The content group node of the skia list view that contains the list items which are translated based on the scroll position.

matrix: SharedValue<SkMatrix>

The Skia Matrix that translates the content node.

  • matrix.value[0] is the X scale.
  • matrix.value[4] is the Y scale.
  • matrix.value[5] is the Y translation. Use scrollY instead
  • matrix.value[2] is the X translation. Use safeArea.left instead
  • matrix.value[1] is the X skew.
  • matrix.value[3] is the Y skew.

redraw: () => void

Call redraw() to request a redraw of the skia canvas, e.g. when adding a fixed element to the root node.
When using FlatList use redrawItems() instead to redraw the list items.
When animating a property use startedAnimation() and finishedAnimation() to efficiently rerender the list.

pressing: SharedValue<boolean>

Shared value that indicates if the view is currently being pressed.


SkiaScrollViewProps

customScrollGesture?: typeof getScrollGesture

Specify a custom scroll gesture.

customGesture?: (props) => ComposedGesture

Specify a custom gesture handler. E.g. to implement scrolling and swiping list items horizontally.

safeArea?: EdgeInsets

Specify offsets for the content of the scroll view.

e.g. { top: 30, bottom: 20, left: 15, right: 15 }

automaticallyAdjustKeyboardInsets?: boolean

Set to false to disable the automatic keyboard adjustment.

keyboard?: ReanimatedContext

Specify a custom keyboard handler

inverted?: boolean

SkiaScrollView so that the elements start rendering from the bottom screen to the top.

mode?: "default" | "continuous"

manually overwrite the rendering mode. The default value is undefined meaning it only rerenders when needed