---
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
```
***