--- title: Index table description: >- Most apps need a way for merchants to manage collections of items. The index table composition provides a scannable list with search, filtering, sorting, and bulk actions. api_name: app-home source_url: html: 'https://shopify.dev/docs/api/app-home/patterns/compositions/index-table' md: 'https://shopify.dev/docs/api/app-home/patterns/compositions/index-table.md' --- # Index table Most apps need a way for merchants to manage collections of items. The index table composition provides a scannable list with search, filtering, sorting, and bulk actions. Use checkboxes for bulk selection, reveal row actions on hover, and include pagination for large data sets. This composition follows proven design guidelines that help your app feel native to the Shopify admin. See [Built for Shopify requirements](https://shopify.dev/docs/apps/launch/built-for-shopify/requirements) for more details on these guidelines for apps. #### Use cases * Displaying any collection of similar items that merchants need to manage * Providing search and filter capabilities for large data sets * Enabling bulk operations like activating, archiving, or deleting multiple items *** ## Examples ### Display an index table with search, sort, and bulk actions Merchants need to view, search, filter, and take bulk actions on collections of items. This pattern displays an index table with search, sort, and bulk actions. Key attributes include `slot="filters"` on the [grid](https://shopify.dev/docs/api/app-home/web-components/layout-and-structure/grid) to place controls in the filters area, `clickDelegate` on [table rows](https://shopify.dev/docs/api/app-home/web-components/layout-and-structure/table) to connect clicks to checkboxes, and `listSlot` on [table headers](https://shopify.dev/docs/api/app-home/web-components/layout-and-structure/table) to control responsive stacking. Use the [Navigation API](https://shopify.dev/docs/api/app-home/apis/navigation) for programmatic navigation and the [Toast API](https://shopify.dev/docs/api/app-home/apis/toast) for confirming successful actions. ##### jsx ```tsx Sort Puzzle name Pieces Created Status A-Z Z-A Puzzle Pieces Created Status Mountain View 16 Today Active Ocean Sunset 9 Yesterday Active Forest Animals 25 Last week Draft ``` ##### html ```html Sort Puzzle name Pieces Created Status A-Z Z-A Puzzle Pieces Created Status Mountain View 16 Today Active Ocean Sunset 9 Yesterday Active Forest Animals 25 Last week Draft ``` ### Add pagination Table with pagination controls for navigating large datasets. ##### jsx ```tsx // @framework-globals: useState const [currentPage, setCurrentPage] = useState(1); const totalPages = 3; const puzzlesByPage = { 1: [ {id: 'mountain-view', name: 'Mountain View', pieces: 16, created: 'Jan 15', status: 'Active', img: 29}, {id: 'ocean-sunset', name: 'Ocean Sunset', pieces: 9, created: 'Jan 14', status: 'Active', img: 12}, {id: 'forest-animals', name: 'Forest Animals', pieces: 25, created: 'Jan 12', status: 'Draft', img: 324}, ], 2: [ {id: 'city-skyline', name: 'City Skyline', pieces: 36, created: 'Jan 10', status: 'Active', img: 1031}, {id: 'autumn-leaves', name: 'Autumn Leaves', pieces: 16, created: 'Jan 8', status: 'Active', img: 167}, {id: 'northern-lights', name: 'Northern Lights', pieces: 49, created: 'Jan 5', status: 'Draft', img: 360}, ], 3: [ {id: 'tropical-beach', name: 'Tropical Beach', pieces: 9, created: 'Jan 3', status: 'Active', img: 1015}, {id: 'snowy-mountains', name: 'Snowy Mountains', pieces: 64, created: 'Dec 28', status: 'Active', img: 870}, {id: 'flower-garden', name: 'Flower Garden', pieces: 25, created: 'Dec 22', status: 'Archived', img: 106}, ], }; const currentPuzzles = puzzlesByPage[currentPage]; const handlePreviousPage = () => { if (currentPage > 1) setCurrentPage(currentPage - 1); }; const handleNextPage = () => { if (currentPage < totalPages) setCurrentPage(currentPage + 1); }; return ( 1} hasNextPage={currentPage < totalPages} onPreviousPage={handlePreviousPage} onNextPage={handleNextPage} paginationLabel={`Page ${currentPage} of ${totalPages}`} > Puzzle Pieces Created Status {currentPuzzles.map((puzzle) => ( {puzzle.name} {puzzle.pieces} {puzzle.created} {puzzle.status} ))} ); ``` ##### html ```html Puzzle Pieces Created Status City Skyline 36 Jan 10 Active Autumn Leaves 16 Jan 8 Active Northern Lights 49 Jan 5 Draft ``` ### Confirm bulk actions with Modal Use the [Modal API](https://shopify.dev/docs/api/app-home/apis/modal-api) to confirm destructive bulk actions before executing them. ##### jsx ```tsx // @framework-globals: useState const puzzles = [ { id: 'mountain-view', name: 'Mountain View', pieces: 16, created: 'Today', status: 'Active', image: 'https://picsum.photos/id/29/80/80' }, { id: 'ocean-sunset', name: 'Ocean Sunset', pieces: 9, created: 'Yesterday', status: 'Active', image: 'https://picsum.photos/id/12/80/80' }, { id: 'forest-animals', name: 'Forest Animals', pieces: 25, created: 'Last week', status: 'Draft', image: 'https://picsum.photos/id/324/80/80' }, ]; const [selectedIds, setSelectedIds] = useState([]); const allSelected = selectedIds.length === puzzles.length; const someSelected = selectedIds.length > 0 && selectedIds.length < puzzles.length; const noneSelected = selectedIds.length === 0; const handleSelectAll = () => { if (allSelected) { setSelectedIds([]); } else { setSelectedIds(puzzles.map(p => p.id)); } }; const handleSelectRow = (id) => { if (selectedIds.includes(id)) { setSelectedIds(selectedIds.filter(i => i !== id)); } else { setSelectedIds([...selectedIds, id]); } }; const handleBulkAction = (action) => { console.log(`${action} items:`, selectedIds); // Perform bulk action here }; return ( {/* Bulk action bar - appears when items are selected */} {!noneSelected && ( {selectedIds.length} of {puzzles.length} selected handleBulkAction('edit')}> Bulk edit handleBulkAction('delete')}> Delete )} {/* Search filters - hidden when bulk action bar is visible */} {noneSelected && ( Sort )} Puzzle Pieces Created Status {puzzles.map((puzzle) => ( handleSelectRow(puzzle.id)} > handleSelectRow(puzzle.id)} accessibilityLabel={`Select ${puzzle.name}`} /> {puzzle.name} {puzzle.pieces} {puzzle.created} {puzzle.status} ))} ); ``` ##### html ```html 2 of 3 selected Bulk edit Delete Puzzle Pieces Created Status Mountain View 16 Today Active Ocean Sunset 9 Yesterday Active Forest Animals 25 Last week Draft ``` ### Navigate to detail pages Use `href` attributes on row elements to navigate merchants to detail pages when they click a row. ##### jsx ```tsx Puzzle name Pieces Puzzle Pieces Status Mountain View 16 Active Ocean Sunset 9 Active ``` ##### html ```html Puzzle name Pieces Puzzle Pieces Status Mountain View 16 Active Ocean Sunset 9 Active ``` ### Show bulk action feedback with Toast Use the [Toast API](https://shopify.dev/docs/api/app-home/apis/toast) to show feedback when bulk actions complete. ##### jsx ```tsx // @validate-ignore: Type 'string' is not assignable to type 'Lowercase' Puzzle name Pieces { shopify.toast.show('2 puzzles archived'); }} > Archive Delete Puzzle Pieces Status Mountain View 16 Active Ocean Sunset 9 Active Are you sure you want to delete the selected puzzles? This action cannot be undone. { shopify.toast.show('2 puzzles deleted'); }} > Delete Cancel ``` ##### html ```html Puzzle name Pieces Archive Delete Puzzle Pieces Status Mountain View 16 Active Ocean Sunset 9 Active Are you sure you want to delete the selected puzzles? This action cannot be undone. Delete Cancel ``` ***