Choose the one you need depending on two criteria: content and UI behavior. When it comes to content, you can choose between a Stories or a Clips version of the list. As far as UI is concerned, you have 2 options:
Rows are horizontal scrolling lists.
Grids are vertical lists, organized into columns (number of columns can be set on the Theme)
The Storyteller SDK provides two components to display stories: StorytellerStoriesRowView and StorytellerStoriesGridView. Most props are shared. Differences:
The shared props between StorytellerStoriesRowView and StorytellerStoriesGridView are the following:
importtype{CellType,UIStyle,Theme,DataLoadCompletedEvent}from'@getstoryteller/react-native-storyteller-sdk';// Component props{configuration:{categories?:Array<string>;displayLimit?:number;cellType?:CellType;theme?:Theme;uiStyle?:UIStyle;// Row-only: fine-tune horizontal layout by fixing tiles visible at oncevisibleTiles?:number;// Grid-only: control internal scroll handling of the grid (default false)isScrollable?:boolean;};onDataLoadStarted?:()=>void;onDataLoadCompleted?:(event:DataLoadCompletedEvent)=>void;onPlayerDismissed?:()=>void;onTileTapped?:(event:{id:string})=>void;style:ViewStyle;}
configuration.categories: assigns a list of Story categories to be displayed inside a row or grid
configuration.displayLimit: limit number of cells to be displayed in a row or grid
configuration.cellType: the style of a cell, use CellType.round or CellType.square. Default is CellType.square
configuration.theme: use this property to customize the appearance of the Storyteller List and its various UI elements. You can read more about the various properties in Themes. There is also an example of this in the Showcase App.
configuration.uiStyle: sets if Storyteller is rendered in light or dark mode, use UIStyle.auto, UIStyle.light, or UIStyle.dark. Default is UIStyle.auto.
configuration.visibleTiles (Row only): number of tiles visible on screen at once (optional, for fine-tuning layout)
configuration.isScrollable (Grid only): whether the grid should handle its own scrolling. Default is false. Set to true for standalone scrollable grids, or false when embedding in a parent ScrollView.
importReact,{useRef}from'react';import{StyleSheet}from'react-native';import{StorytellerStoriesRowView,CellType,UIStyle,typeStorytellerStoriesRowViewInterface,typeDataLoadCompletedEvent,}from'@getstoryteller/react-native-storyteller-sdk';constMyStoriesRow=()=>{// Ensure StorytellerSDK is initializedconstrowRef=useRef<StorytellerStoriesRowViewInterface>(null);useEffect(()=>{if(isInitialized){rowRef.current?.reloadData();}},[isInitialized]);consthandleDataLoadStarted=()=>{console.log('Stories data load started');};consthandleDataLoadCompleted=(event:DataLoadCompletedEvent)=>{console.log('Stories data load completed:',event);if(event.success){console.log(`Loaded ${event.dataCount} stories`);}else{console.error('Error loading data:',event.error);}};consthandlePlayerDismissed=()=>{console.log('Stories player dismissed');};consthandleTileTapped=(event:{id:string})=>{console.log('Story tile tapped:',event.id);};return(<StorytellerStoriesRowViewref={rowRef}configuration={{categories:['category1','category2'],displayLimit:10,cellType:CellType.round,uiStyle:UIStyle.auto,}}style={styles.container}onDataLoadStarted={handleDataLoadStarted}onDataLoadCompleted={handleDataLoadCompleted}onPlayerDismissed={handlePlayerDismissed}onTileTapped={handleTileTapped}/>);};conststyles=StyleSheet.create({container:{height:150,},});exportdefaultMyStoriesRow;
The Storyteller SDK provides two components to display clips: StorytellerClipsRowView and StorytellerClipsGridView. Most props are shared. Differences:
configuration.collection: assigns a collection to be displayed inside a row or grid
configuration.displayLimit: limit number of cells to be displayed in a row or grid
configuration.cellType: the style of a cell, use CellType.round or CellType.square. Default is CellType.square
configuration.theme: use this property to customize the appearance of the Storyteller List and its various UI elements. You can read more about the various properties in Themes. There is also an example of this in the Showcase App.
configuration.uiStyle: sets if Storyteller is rendered in light or dark mode, use UIStyle.auto, UIStyle.light, or UIStyle.dark. Default is UIStyle.auto.
configuration.visibleTiles (Row only): number of tiles visible on screen at once (optional, for fine-tuning layout)
configuration.isScrollable: (Grid view only) whether the grid should handle its own scrolling. Default is false. Set to true for standalone scrollable grids, or false when embedding in a parent ScrollView.
Performance Note: Non-scrollable grids are suitable for when you wish to include the grid in a view hierarchy that already supports scrolling. Using non-scrollable grids with a large number of items and no display limit can significantly degrade performance, as all items are rendered in one go.
importReact,{useRef}from'react';import{StyleSheet}from'react-native';import{StorytellerClipsRowView,CellType,UIStyle,typeStorytellerClipsRowViewInterface,typeDataLoadCompletedEvent,}from'@getstoryteller/react-native-storyteller-sdk';constMyClipsRow=()=>{// Ensure StorytellerSDK is initializedconstrowRef=useRef<StorytellerClipsRowViewInterface>(null);useEffect(()=>{if(isInitialized){rowRef.current?.reloadData();}},[isInitialized]);consthandleDataLoadStarted=()=>{console.log('Clips data load started');};consthandleDataLoadCompleted=(event:DataLoadCompletedEvent)=>{console.log('Clips data load completed:',event);if(event.success){console.log(`Loaded ${event.dataCount} clips`);}else{console.error('Error loading data:',event.error);}};consthandlePlayerDismissed=()=>{console.log('Clips player dismissed');};consthandleTileTapped=(event:{id:string})=>{console.log('Clip tile tapped:',event.id);};return(<StorytellerClipsRowViewref={rowRef}configuration={{collection:'my-collection-id',displayLimit:10,cellType:CellType.square,uiStyle:UIStyle.auto,}}style={styles.container}onDataLoadStarted={handleDataLoadStarted}onDataLoadCompleted={handleDataLoadCompleted}onPlayerDismissed={handlePlayerDismissed}onTileTapped={handleTileTapped}/>);};conststyles=StyleSheet.create({container:{height:200,},});exportdefaultMyClipsRow;
Choose the appropriate scrolling behavior based on your layout:
Scrollable (isScrollable: true):
Use when the list is the primary scrolling content
Better performance for long lists
Handles its own scroll events
Non-Scrollable (isScrollable: false - default):
Use when embedding within a parent ScrollView or FlashList
Renders all items within displayLimit at once
No internal scroll handling
// Embedded in parent ScrollView<ScrollView><Text>HeaderContent</Text><StorytellerStoriesRowViewconfiguration={{categories:['featured'],displayLimit:10}}style={{height:200}}/><Text>MoreContent</Text></ScrollView>
{"slug": "storyteller-list-views", "page_title": "List Components", "page_url": "StorytellerListViews/", "canonical_url": "/react-native/StorytellerListViews/", "markdown": "# The Storyteller List Views\n\nWe have 4 flavors of final List views that you can use, that have the following class hierarchy (root class is at the top):\n\n```text\n StorytellerListView\n \u251c\u2500\u2500 StorytellerRowView\n \u2502 \u251c\u2500\u2500 StorytellerStoriesRowView\n \u2502 \u2514\u2500\u2500 StorytellerClipsRowView\n \u2514\u2500\u2500 StorytellerGridView\n \u251c\u2500\u2500 StorytellerStoriesGridView\n \u2514\u2500\u2500 StorytellerClipsGridView\n```\n\nChoose the one you need depending on two criteria: content and UI behavior. When it comes to content, you can choose between a **Stories** or a **Clips** version of the list. As far as UI is concerned, you have 2 options:\n\n**Rows** are horizontal scrolling lists.\n\n**Grids** are vertical lists, organized into columns (number of columns can be set on the [Theme](Themes.md))\n\n## Configuring a Storyteller Stories List Component\n\nThe Storyteller SDK provides two components to display stories: `StorytellerStoriesRowView` and `StorytellerStoriesGridView`. Most props are shared. Differences:\n\n- `visibleTiles` is supported only on Row views\n- `isScrollable` is supported only on Grid views\n\n### Props\n\nThe shared props between `StorytellerStoriesRowView` and `StorytellerStoriesGridView` are the following:\n\n```typescript\nimport type {\n CellType,\n UIStyle,\n Theme,\n DataLoadCompletedEvent\n} from '@getstoryteller/react-native-storyteller-sdk';\n\n// Component props\n{\n configuration: {\n categories?: Array<string>;\n displayLimit?: number;\n cellType?: CellType;\n theme?: Theme;\n uiStyle?: UIStyle;\n // Row-only: fine-tune horizontal layout by fixing tiles visible at once\n visibleTiles?: number;\n // Grid-only: control internal scroll handling of the grid (default false)\n isScrollable?: boolean;\n };\n onDataLoadStarted?: () => void;\n onDataLoadCompleted?: (event: DataLoadCompletedEvent) => void;\n onPlayerDismissed?: () => void;\n onTileTapped?: (event: { id: string }) => void;\n style: ViewStyle;\n}\n```\n\n#### Configuration Properties\n\n- `configuration.categories`: assigns a list of Story categories to be displayed inside a row or grid\n- `configuration.displayLimit`: limit number of cells to be displayed in a row or grid\n- `configuration.cellType`: the style of a cell, use `CellType.round` or `CellType.square`. Default is `CellType.square`\n- `configuration.theme`: use this property to customize the appearance of the Storyteller List and its various UI elements. You can read more about the various properties in [Themes](Themes.md). There is also an example of this in the [Showcase App](Showcase.md).\n- `configuration.uiStyle`: sets if Storyteller is rendered in light or dark mode, use `UIStyle.auto`, `UIStyle.light`, or `UIStyle.dark`. Default is `UIStyle.auto`.\n- `configuration.visibleTiles` (Row only): number of tiles visible on screen at once (optional, for fine-tuning layout)\n- `configuration.isScrollable` (Grid only): whether the grid should handle its own scrolling. Default is `false`. Set to `true` for standalone scrollable grids, or `false` when embedding in a parent ScrollView.\n\n#### Callbacks\n\nCallbacks used for `StorytellerListViewDelegate` methods are the following props:\n\n- `onDataLoadStarted`: called when the SDK begins loading Story data - this could be used as a trigger to show a loading spinner in your app, for example\n- `onDataLoadCompleted`: called when the SDK finishes loading Story data. The callback receives a `DataLoadCompletedEvent` object with:\n + `success` (boolean): whether the data load was successful\n + `error` (string): error message if the load failed (empty string if successful)\n + `dataCount` (number): number of Stories loaded\n- `onPlayerDismissed`: called when a user exits the Story player view\n- `onTileTapped`: called when a user taps on a story tile. The callback receives an object with:\n + `id` (string): the ID of the tapped story\n\nYou can find more information about `StorytellerListViewDelegate` on [iOS](https://www.getstoryteller.com/documentation/ios/storyteller-list-view-delegate) and [Android](https://www.getstoryteller.com/documentation/android/storyteller-list-view-delegate)\n\n### Adding StorytellerStoriesRowView in the layout\n\n=== \"v11.0.0+\"\n\n ```tsx\n import React, { useRef } from 'react';\n import { StyleSheet } from 'react-native';\n import {\n StorytellerStoriesRowView,\n CellType,\n UIStyle,\n type StorytellerStoriesRowViewInterface,\n type DataLoadCompletedEvent,\n } from '@getstoryteller/react-native-storyteller-sdk';\n\n const MyStoriesRow = () => {\n // Ensure StorytellerSDK is initialized\n const rowRef = useRef<StorytellerStoriesRowViewInterface>(null);\n\n useEffect(() => {\n if (isInitialized) {\n rowRef.current?.reloadData();\n }\n }, [isInitialized]);\n\n const handleDataLoadStarted = () => {\n console.log('Stories data load started');\n };\n\n const handleDataLoadCompleted = (event: DataLoadCompletedEvent) => {\n console.log('Stories data load completed:', event);\n if (event.success) {\n console.log(`Loaded ${event.dataCount} stories`);\n } else {\n console.error('Error loading data:', event.error);\n }\n };\n\n const handlePlayerDismissed = () => {\n console.log('Stories player dismissed');\n };\n\n const handleTileTapped = (event: { id: string }) => {\n console.log('Story tile tapped:', event.id);\n };\n\n return (\n <StorytellerStoriesRowView\n ref={rowRef}\n configuration={{\n categories: ['category1', 'category2'],\n displayLimit: 10,\n cellType: CellType.round,\n uiStyle: UIStyle.auto,\n }}\n style={styles.container}\n onDataLoadStarted={handleDataLoadStarted}\n onDataLoadCompleted={handleDataLoadCompleted}\n onPlayerDismissed={handlePlayerDismissed}\n onTileTapped={handleTileTapped}\n />\n );\n };\n\n const styles = StyleSheet.create({\n container: {\n height: 150,\n },\n });\n\n export default MyStoriesRow;\n ```\n\n=== \"v10.x\"\n\n ```typescript\n private reference: StorytellerStoriesRowView | null = null;\n\n _onDataLoadStarted = () => {\n console.log(`DataLoadStarted`);\n };\n\n _onDataLoadCompleted = (\n success: Boolean,\n error: Error,\n dataCount: number\n ) => {\n console.log(\n `DataLoadCompleted\\n` +\n `success: ${success}, error: ${error}, dataCount: ${dataCount}`\n );\n };\n\n reloadData = () => {\n reference.reloadData();\n };\n\n render() {\n return (\n <StorytellerStoriesRowView\n ref={(ref: any) => {\n if (ref) this.reference = ref;\n }}\n configuration={{\n categories: this.state.categories,\n cellType: this.state.cellType,\n theme: this.state.theme\n }}\n style={styles.container}\n onDataLoadStarted={this._onDataLoadStarted}\n onDataLoadCompleted={this._onDataLoadCompleted}\n />\n );\n }\n ```\n\n### Adding StorytellerStoriesGridView in the layout\n\n=== \"v11.0.0+\"\n\n ```tsx\n import React, { useRef } from 'react';\n import { StyleSheet } from 'react-native';\n import {\n StorytellerStoriesGridView,\n CellType,\n UIStyle,\n type StorytellerStoriesGridViewInterface,\n type DataLoadCompletedEvent,\n } from '@getstoryteller/react-native-storyteller-sdk';\n\n const MyStoriesGrid = () => {\n // Ensure StorytellerSDK is initialized\n const gridRef = useRef<StorytellerStoriesGridViewInterface>(null);\n\n useEffect(() => {\n if (isInitialized) {\n gridRef.current?.reloadData();\n }\n }, [isInitialized]);\n\n const handleDataLoadStarted = () => {\n console.log('Stories grid data load started');\n };\n\n const handleDataLoadCompleted = (event: DataLoadCompletedEvent) => {\n console.log('Stories grid data load completed:', event);\n if (event.success) {\n console.log(`Loaded ${event.dataCount} stories`);\n } else {\n console.error('Error loading data:', event.error);\n }\n };\n\n const handlePlayerDismissed = () => {\n console.log('Stories grid player dismissed');\n };\n\n const handleTileTapped = (event: { id: string }) => {\n console.log('Story tile tapped:', event.id);\n };\n\n return (\n <StorytellerStoriesGridView\n ref={gridRef}\n configuration={{\n categories: ['category1', 'category2'],\n displayLimit: 8,\n cellType: CellType.square,\n uiStyle: UIStyle.auto,\n isScrollable: true, // optional, default is false\n }}\n style={styles.container}\n onDataLoadStarted={handleDataLoadStarted}\n onDataLoadCompleted={handleDataLoadCompleted}\n onPlayerDismissed={handlePlayerDismissed}\n onTileTapped={handleTileTapped}\n />\n );\n };\n\n const styles = StyleSheet.create({\n container: {\n minHeight: 400,\n },\n });\n\n export default MyStoriesGrid;\n ```\n\n=== \"v10.x\"\n\n ```typescript\n reference: StorytellerStoriesGridView | null = null;\n\n _onDataLoadStarted = () => {\n console.log(`DataLoadStarted`);\n };\n\n _onDataLoadCompleted = (\n success: Boolean,\n error: Error,\n dataCount: number\n ) => {\n console.log(\n `DataLoadCompleted\\n` +\n `success: ${success}, error: ${error}, dataCount: ${dataCount}`\n );\n };\n\n reloadData = () => {\n this.reference?.reloadData();\n };\n\n render() {\n return (\n <StorytellerStoriesGridView\n ref={(ref: any) => {\n if (ref) this.reference = ref;\n }}\n configuration={{\n categories: this.state.categories,\n displayLimit: this.state.displayLimit,\n cellType: this.state.cellType,\n theme: this.state.theme\n }}\n style={styles.container}\n onDataLoadStarted={this._onDataLoadStarted}\n onDataLoadCompleted={this._onDataLoadCompleted}\n />\n );\n }\n ```\n\n## Configuring a Storyteller Clips List Component\n\nThe Storyteller SDK provides two components to display clips: `StorytellerClipsRowView` and `StorytellerClipsGridView`. Most props are shared. Differences:\n\n- `visibleTiles` is supported only on Row views\n- `isScrollable` is supported only on Grid views\n\n### Props\n\nThe shared props between `StorytellerClipsRowView` and `StorytellerClipsGridView` are defined by React component props. Here's the structure:\n\n```typescript\nimport type {\n CellType,\n UIStyle,\n Theme,\n DataLoadCompletedEvent\n} from '@getstoryteller/react-native-storyteller-sdk';\n\n// Component props\n{\n configuration: {\n collection?: string;\n displayLimit?: number;\n cellType?: CellType;\n theme?: Theme;\n uiStyle?: UIStyle;\n // Row-only\n visibleTiles?: number;\n // Grid-only (default false)\n isScrollable?: boolean;\n };\n onDataLoadStarted?: () => void;\n onDataLoadCompleted?: (event: DataLoadCompletedEvent) => void;\n onPlayerDismissed?: () => void;\n onTileTapped?: (event: { id: string }) => void;\n style: ViewStyle;\n}\n```\n\n#### Configuration Properties\n\n- `configuration.collection`: assigns a collection to be displayed inside a row or grid\n- `configuration.displayLimit`: limit number of cells to be displayed in a row or grid\n- `configuration.cellType`: the style of a cell, use `CellType.round` or `CellType.square`. Default is `CellType.square`\n- `configuration.theme`: use this property to customize the appearance of the Storyteller List and its various UI elements. You can read more about the various properties in [Themes](Themes.md). There is also an example of this in the [Showcase App](Showcase.md).\n- `configuration.uiStyle`: sets if Storyteller is rendered in light or dark mode, use `UIStyle.auto`, `UIStyle.light`, or `UIStyle.dark`. Default is `UIStyle.auto`.\n- `configuration.visibleTiles` (Row only): number of tiles visible on screen at once (optional, for fine-tuning layout)\n- `configuration.isScrollable`: (Grid view only) whether the grid should handle its own scrolling. Default is `false`. Set to `true` for standalone scrollable grids, or `false` when embedding in a parent ScrollView.\n\n**Performance Note**: Non-scrollable grids are suitable for when you wish to include the grid in a view hierarchy that already supports scrolling. Using non-scrollable grids with a large number of items and no display limit can significantly degrade performance, as all items are rendered in one go.\n\n#### Callbacks\n\nCallbacks used for `StorytellerListViewDelegate` methods are the following props:\n\n- `onDataLoadStarted`: called when the SDK begins loading Clip data - this could be used as a trigger to show a loading spinner in your app, for example\n- `onDataLoadCompleted`: called when the SDK finishes loading Clip data. The callback receives a `DataLoadCompletedEvent` object with:\n + `success` (boolean): whether the data load was successful\n + `error` (string): error message if the load failed (empty string if successful)\n + `dataCount` (number): number of Clips loaded\n- `onPlayerDismissed`: called when a user exits the Clip player view\n- `onTileTapped`: called when a user taps on a clip tile. The callback receives an object with:\n + `id` (string): the ID of the tapped clip\n\nYou can find more information about `StorytellerListViewDelegate` on [iOS](https://www.getstoryteller.com/documentation/ios/storyteller-list-view-delegate) and [Android](https://www.getstoryteller.com/documentation/android/storyteller-list-view-delegate)\n\n### Adding StorytellerClipsRowView in the layout\n\n=== \"v11.0.0+\"\n\n ```tsx\n import React, { useRef } from 'react';\n import { StyleSheet } from 'react-native';\n import {\n StorytellerClipsRowView,\n CellType,\n UIStyle,\n type StorytellerClipsRowViewInterface,\n type DataLoadCompletedEvent,\n } from '@getstoryteller/react-native-storyteller-sdk';\n\n const MyClipsRow = () => {\n // Ensure StorytellerSDK is initialized\n const rowRef = useRef<StorytellerClipsRowViewInterface>(null);\n\n useEffect(() => {\n if (isInitialized) {\n rowRef.current?.reloadData();\n }\n }, [isInitialized]);\n\n const handleDataLoadStarted = () => {\n console.log('Clips data load started');\n };\n\n const handleDataLoadCompleted = (event: DataLoadCompletedEvent) => {\n console.log('Clips data load completed:', event);\n if (event.success) {\n console.log(`Loaded ${event.dataCount} clips`);\n } else {\n console.error('Error loading data:', event.error);\n }\n };\n\n const handlePlayerDismissed = () => {\n console.log('Clips player dismissed');\n };\n\n const handleTileTapped = (event: { id: string }) => {\n console.log('Clip tile tapped:', event.id);\n };\n\n return (\n <StorytellerClipsRowView\n ref={rowRef}\n configuration={{\n collection: 'my-collection-id',\n displayLimit: 10,\n cellType: CellType.square,\n uiStyle: UIStyle.auto,\n }}\n style={styles.container}\n onDataLoadStarted={handleDataLoadStarted}\n onDataLoadCompleted={handleDataLoadCompleted}\n onPlayerDismissed={handlePlayerDismissed}\n onTileTapped={handleTileTapped}\n />\n );\n };\n\n const styles = StyleSheet.create({\n container: {\n height: 200,\n },\n });\n\n export default MyClipsRow;\n ```\n\n=== \"v10.x\"\n\n ```typescript\n private reference: StorytellerClipsRowView | null = null;\n\n _onDataLoadStarted = () => {\n console.log(`DataLoadStarted`);\n };\n\n _onDataLoadCompleted = (\n success: Boolean,\n error: Error,\n dataCount: number\n ) => {\n console.log(\n `DataLoadCompleted\\n` +\n `success: ${success}, error: ${error}, dataCount: ${dataCount}`\n );\n };\n\n reloadData = () => {\n reference.reloadData();\n };\n\n render() {\n return (\n <StorytellerClipsRowView\n ref={(ref: any) => {\n if (ref) this.reference = ref;\n }}\n configuration={{\n collection: this.state.collection,\n cellType: this.state.cellType,\n theme: this.state.theme\n }}\n style={styles.container}\n onDataLoadStarted={this._onDataLoadStarted}\n onDataLoadCompleted={this._onDataLoadCompleted}\n />\n );\n }\n ```\n\n### Adding StorytellerClipsGridView in the layout\n\n=== \"v11.0.0+\"\n\n ```tsx\n import React, { useRef } from 'react';\n import { StyleSheet } from 'react-native';\n import {\n StorytellerClipsGridView,\n CellType,\n UIStyle,\n type StorytellerClipsGridViewInterface,\n type DataLoadCompletedEvent,\n } from '@getstoryteller/react-native-storyteller-sdk';\n\n const MyClipsGrid = () => {\n // Ensure StorytellerSDK is initialized\n const gridRef = useRef<StorytellerClipsGridViewInterface>(null);\n\n useEffect(() => {\n if (isInitialized) {\n gridRef.current?.reloadData();\n }\n }, [isInitialized]);\n\n const handleDataLoadStarted = () => {\n console.log('Clips grid data load started');\n };\n\n const handleDataLoadCompleted = (event: DataLoadCompletedEvent) => {\n console.log('Clips grid data load completed:', event);\n if (event.success) {\n console.log(`Loaded ${event.dataCount} clips`);\n } else {\n console.error('Error loading data:', event.error);\n }\n };\n\n const handlePlayerDismissed = () => {\n console.log('Clips grid player dismissed');\n };\n\n const handleTileTapped = (event: { id: string }) => {\n console.log('Clip tile tapped:', event.id);\n };\n\n return (\n <StorytellerClipsGridView\n ref={gridRef}\n configuration={{\n collection: 'my-collection-id',\n displayLimit: 10,\n cellType: CellType.square,\n uiStyle: UIStyle.auto,\n isScrollable: true, // optional, default is false\n }}\n style={styles.container}\n onDataLoadStarted={handleDataLoadStarted}\n onDataLoadCompleted={handleDataLoadCompleted}\n onPlayerDismissed={handlePlayerDismissed}\n onTileTapped={handleTileTapped}\n />\n );\n };\n\n const styles = StyleSheet.create({\n container: {\n minHeight: 400,\n },\n });\n\n export default MyClipsGrid;\n ```\n\n=== \"v10.x\"\n\n ```typescript\n reference: StorytellerClipsGridView | null = null;\n\n _onDataLoadStarted = () => {\n console.log(`DataLoadStarted`);\n };\n\n _onDataLoadCompleted = (\n success: Boolean,\n error: Error,\n dataCount: number\n ) => {\n console.log(\n `DataLoadCompleted\\n` +\n `success: ${success}, error: ${error}, dataCount: ${dataCount}`\n );\n };\n\n reloadData = () => {\n reference.reloadData();\n };\n\n render() {\n return (\n <StorytellerClipsGridView\n ref={(ref: any) => {\n if (ref) this.reference = ref;\n }}\n configuration={{\n collection: this.state.collection,\n displayLimit: this.state.displayLimit,\n cellType: this.state.cellType,\n theme: this.state.theme\n }}\n style={styles.container}\n onDataLoadStarted={this._onDataLoadStarted}\n onDataLoadCompleted={this._onDataLoadCompleted}\n />\n );\n }\n ```\n\n## Imperative API: reloadData()\n\nAll Storyteller list components expose a `reloadData()` method through refs, which allows you to manually refresh the component's data.\n\n### When to Use reloadData()\n\n- **On mount**: When the component is mounted, you should call `reloadData()` to load the data into your Storyteller component.\n- **User-triggered refresh**: When the user pulls to refresh or taps a refresh button\n- **Data invalidation**: When you know the underlying data has changed (e.g., after following a category)\n- **Configuration changes**: After changing categories, collections, or other configuration properties\n- **Network recovery**: After recovering from a network error\n\n### Performance Considerations\n\n- **Avoid excessive calls**: Don't call `reloadData()` too frequently (e.g., multiple times per second)\n- **Network impact**: Each call triggers a new network request to fetch data\n- **User experience**: Consider showing loading indicators during refresh operations\n\n## Performance Best Practices\n\n### Display Limits\n\nThe `displayLimit` configuration property helps control memory usage and rendering performance:\n\n```tsx\n<StorytellerStoriesGridView\n configuration={{\n categories: ['featured'],\n displayLimit: 20, // Limit to 20 items\n }}\n style={{ minHeight: 400 }}\n/>\n```\n\n**Guidelines**:\n\n- **Rows**: Limit to 10-20 items for optimal scrolling performance\n- **Grids**: Limit to 20-50 items depending on device capabilities\n- **Non-scrollable grids**: Always use `displayLimit` to prevent rendering all items at once\n\n### Scrollable vs Non-Scrollable\n\nChoose the appropriate scrolling behavior based on your layout:\n\n**Scrollable (`isScrollable: true`)**:\n\n- Use when the list is the primary scrolling content\n- Better performance for long lists\n- Handles its own scroll events\n\n**Non-Scrollable (`isScrollable: false` - default)**:\n\n- Use when embedding within a parent ScrollView or FlashList\n- Renders all items within `displayLimit` at once\n- No internal scroll handling\n\n```tsx\n// Embedded in parent ScrollView\n<ScrollView>\n <Text>Header Content<\/Text>\n<StorytellerStoriesRowView\n configuration={{ categories: ['featured'], displayLimit: 10 }}\n style={{ height: 200 }}\n/>\n <Text>More Content<\/Text>\n<\/ScrollView>\n```\n\n### Memory Management Tips\n\n1. **Lazy Loading**: Use `displayLimit` to avoid loading too many items\n2. **Category Filtering**: Only load categories that users are interested in\n3. **Conditional Rendering**: Don't render Storyteller views that are far off-screen\n4. **Theme Optimization**: Avoid complex custom themes when possible\n\n## More Advanced Layout\n\nMore advanced examples of layouts where multiple Storyteller Rows and Grids are used can be viewed in our React Native Showcase:\n\n- [`VerticalVideoLists`](https://github.com/getstoryteller/storyteller-showcase-react-native/blob/main/Showcase/src/components/VerticalVideoLists.tsx#L13)\n- [`VerticalVideoListRenderer`](https://github.com/getstoryteller/storyteller-showcase-react-native/blob/main/Showcase/src/components/VerticalVideoListRenderer.tsx#L14)\n- [`StorytellerStoryUnit`](https://github.com/getstoryteller/storyteller-showcase-react-native/blob/main/Showcase/src/components/StorytellerStoryUnit.tsx#L19)\n\n## Notes for Android Implementations\n\nWhen implementing Storyteller views within scrollable lists on Android, there are critical performance and rendering considerations:\n\n### \u26a0\ufe0f Important: FlatList Compatibility Issues\n\nFlatList **must not** be used with Storyteller views on Android. This is because:\n\n- FlatList's virtualization conflicts with Storyteller's native view rendering\n- It can cause crashes and other rendering issues\n\n### Recommended Implementation Options\n\n1. **For Simple Lists (Recommended for < 10 items)**\n + Use `ScrollView` for straightforward implementations\n + Best for fixed, smaller lists of content\n\n2. **For Longer Lists (Recommended for 10+ items)**\n + Use [FlashList](https://shopify.github.io/flash-list/)\n + Provides better performance and memory management\n + Fully compatible with Storyteller views\n\nExample using FlashList:\n\n```tsx\nimport { FlashList } from '@shopify/flash-list';\n\nconst YourComponent = () => {\n return (\n <FlashList\n data={yourData}\n renderItem={({ item }) => (\n <StorytellerStoriesRowView\n configuration={item.configuration}\n ...\n />\n )}\n estimatedItemSize={10}\n />\n );\n};\n```\n", "copy_markdown_include_header": false, "base_path": "", "ai_dir": "ai", "missing_payload_behavior": "empty"}