Building a Smooth Image Carousel with FlatList in React Native
Have you ever noticed how often image (and card) carousels appear within mobile applications? Carousels consolidate items within a single horizontally rotating widget. Users can scroll through items by dragging across right or left to preview subsequent or previous items. Displaying items this way preserves vertical screen real estate, and therefore, allows users to have quicker access to the bottom of the view without scrolling down endlessly. React Native comes with several built-in components, such as <FlatList /> and <ScrollView /> , that can be used to quickly implement an image carousel. Unlike ScrollView /> , <FlatList /> renders child components lazily. Since <FlatList /> only stores in memory the items that are about to appear (and removes from memory the items that disappear offscreen), this results in better performance from reduced memory consumption and processing resources. Below, I'm going to show you how to build an image carousel with React Native's <FlatList /> core component. Users will be able to scroll through the items by dragging along the items: Or by clicking an arrow button to move to the next or previous item: To get started, initialize a new React Native project using the TypeScript template : At the root of the project directory, create two new directories: Delete the contents of the App.tsx file, and replace it with the following: ( App.tsx ) The image carousel will showcase six high-quality Unsplash images of various flowers. At a minimum, each item of the carousel must have a unique ID ( id ), a URI to its corresponding image's location ( uri ) and a title for labeling the image ( title ). To enforce these properties, create a definition file to globally expose the ImageCarouselItem interface. ( types/index.d.ts ) Note : Inside of the tsconfig.json file, set the typeRoots compiler option to ["./types"] . Create a new file named ImageCarousel.tsx under the src/components directory: Inside of the src/components/IamgeCarousel.tsx file, define a new functional component named ImageCarousel that accepts the prop data : ( src/components/ImageCarousel.tsx ) data contains the items that will be rendered by the <FlatList /> component. The <FlatList /> component provides a virtualized list that is highly customizable via its many props. Coincidentally, it inherits most of these props from the <ScrollView /> component. At a minimum, the <FlatList /> component requires two props: Let's render a simple horizontal carousel using the least number of props possible. ( src/components/ImageCarousel.tsx ) Each item follows the same layout: a square-shaped cover image with its title overlaid above and positioned at the lower-right corner. Specify the horizontal prop to tell the <FlatList /> component to arrange the items horizontally rather than vertically. Hide the horizontal scroll indicator, and set a keyExtractor function to tell the <FlatList /> component which item property (or set of properties) it can use to track each item for caching and re-ordering purposes. Adjacent items are spaced away from each other by 30px (for each item, 15px of left- and right-margining). If you save these changes and run the React Native project inside of an iOS simulator, then you will see a basic image carousel: In fact, you can scroll through these items by horizontally dragging along them. Let's modify the carousel to snap items in place and translate an item upwards when it approaches the middle of the screen and downwards when it moves towards either edge of the screen. Currently, when the carousel loads the items, the first item begins at the far left end of the screen, not the middle. When you scroll all the way to the last item in the carousel, the last item ends at the far right end of the screen, not the middle. We're going to need two placeholder items, one at the beginning and another at the end of the carousel, to help position the first and last items in the middle of the screen when reaching either end of the carousel. ( src/components/ImageCarousel.tsx ) As we scroll through the items, the item approaching the middle of the screen should be translated upwards to put it front and center as the current item being visited. Inside of renderItem , define an interpolation that maps an item's x-position to its y-translation. ( src/components/ImageCarousel.tsx ) An item appears to move upwards when its y-translation ( CURRENT_ITEM_TRANSLATE_Y ) is smaller compared to other items' y-translations ( CURRENT_ITEM_TRANSLATE_Y * 2 ). This item ( (index - 1) * ITEM_LENGTH ) happens to be the one that appears in the middle of the screen, not the edges. clamp restricts the extrapolation to only within the boundaries of the specific range. For items to snap into place at the end of a scroll action, specify these additional props on the <FlatList /> component. ( src/components/ImageCarousel.tsx ) Putting it altogether... ( src/components/ImageCarousel.tsx ) Reload the application to preview the smooth animations! Let's introduce arrow controls to give users an alternative way to explore the carousel's items. The <FlatList /> component's reference comes with a scrollToIndex method, which tells the component which item in the carousel to scroll to based on the specified index. All we need to do is track the index of the item currently in the view. Increment it when we scroll right and decrement it when we scroll left. To figure out which item is currently in the view, we need to specify these two props on the <FlatList /> component: ( src/components/ImageCarousel.tsx ) For an item to be considered as currently in the view, it must be 100% visible to the user (no part of it hidden off screen). onViewableItemsChanged calls a handler function whenever the items currently in the view have changed and tells us which items are now 100% visible in the view (based on the visibility percent threshold). We'll track the current index via a React ref since we don't need to re-render the component whenever this index changes. ( src/components/ImageCarousel.tsx ) Define the handleOnViewableItemsChanged handler function. This function checks for the item currently in view and sets the ref's value to this item's index value. ( src/components/ImageCarousel.tsx ) Note : The placeholder items are technically 100% in the view when the user moves to the first or last item in the carousel. Therefore, we need to filter out those placeholder items. Note : Wrap the handleOnViewableItemsChanged function in a useCallback hook to avoid the following issue: Now, let's create the arrow controls. Add two flag variables, isNextDisabled and isPrevDisabled , to check whether or not the arrow controls should be disabled or not. For instance, the previous arrow control should be disabled when the current item is the first item in the carousel, and the next arrow control should be disabled when the current item is the last item in the carousel. ( src/components/ImageCarousel.tsx ) Obtain a reference to the <FlatList /> component to access the scrollToIndex method: ( src/components/ImageCarousel.tsx ) Implement the controls' functionality and style them: ( src/components/ImageCarousel.tsx ) For a consistent scrolling motion between adjacent items via the scrollToIndex method, specify the getItemLayout prop on the <FlatList /> component. Since we already know the fixed dimensions of the carousel's items, we should let the <FlatList /> component know these dimensions so that it doesn't need to dynamically measure the items' dimensions and have scrollToIndex always scroll to a target item correctly. ( src/components/ImageCarousel.tsx ) Altogether... ( src/components/ImageCarousel.tsx ) Reload the application. Here's how the carousel should look and behave: For the final version of this project, click this link for the GitHub repository. Try implementing your own image carousel with React Native's <FlatList /> component. For more about building apps with React Native, check out our new course The newline Guide to React Native for JavaScript Developer .