Skip to main content

React Native Skia List

React Native Skia List is a high-performance list component for React Native.
It is built on top of Skia, a powerful 2D graphics library, and is designed to be very fast and efficient.

It does not have a layout engine and you, the library user, are responsible for calculating the layout of list items.
This makes it harder to use than other list libraries, but also makes it very performant and customizable.

You can achieve good looking, interactive and high-performance lists with a lot of items like the following example:

See the following scroll performance comparison between Skia List and Flash List:

See more details on this blog post.

Skia List

Installation

warning

This library is in early development and not ready for production use.

npm install react-native-skia-list

Usage

// needed for SkiaDomApi type
import type {} from "@shopify/react-native-skia/lib/typescript/src/renderer/HostComponents";
const { Skia } =
require("@shopify/react-native-skia/src/") as typeof import("@shopify/react-native-skia/lib/typescript/src/");
import { SkiaFlatList } from "react-native-skia-list";
import { useSafeAreaInsets } from "react-native-safe-area-context";

// Create a Skia ParagraphBuilder that will be used to build the text for each item
const paragraphBuilder = Skia.ParagraphBuilder.Make({
textStyle: {
color: Skia.Color("black"),
fontSize: 20,
},
});

export default function Test() {
const safeArea = useSafeAreaInsets();

return (
<SkiaFlatList
safeArea={safeArea}
// Provide an initialData array that can be serialized and passed to the worklet thread
initialData={() => [0, 1, 2, 3, 4, 5, 6, 7, 8]}
// To optimize performance for the initial mount you can provide a transformItem function
// It will be called once for each item when it is mounted the first time
transformItem={(item, index, id, state) => {
"worklet";

paragraphBuilder.reset(); // reuses the paragraphBuilder for each item

return paragraphBuilder.addText(`Item ${item}`).build();
}}
// renderItem will be called whenever an item visibility changes
renderItem={(item, index, state, element) => {
"worklet";

const { width } = state.layout.value;

item.layout(width); // calculates the paragraph layout

const height = item.getHeight(); // gets the height of the paragraph

// element is a Skia.GroupNode or will be undefined if only the height of the element is needed
if (!element) return height;

element.addChild(
// see the following link for all element types
// https://github.com/Shopify/react-native-skia/blob/5c38b27d72cea9c158290adb7d23c6109369ac2f/packages/skia/src/renderer/HostComponents.ts#L72-L191
SkiaDomApi.ParagraphNode({
paragraph: item,
x: 0,
y: 0,
width,
})
);

return height;
}}
/>
);
}