# Async List URL: https://ark-ui.com/docs/collections/async-list Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/collections/async-list.mdx A hook for managing asynchronous data operations in list collections including loading, filtering, sorting, and pagination. --- The `useAsyncList` hook manages asynchronous data operations for list collections. It provides a comprehensive solution for loading, filtering, sorting, and paginating data with built-in loading states, error handling, and request cancellation. ```tsx import { useAsyncList } from '@ark-ui/react/collection' const list = useAsyncList({ async load({ signal }) { const response = await fetch('/api/users', { signal }) const users = await response.json() return { items: users } }, }) console.log(list.items) // User[] console.log(list.loading) // boolean console.log(list.error) // Error | null ``` ## Examples ### Basic Usage The most basic usage involves providing a `load` function that returns data. **Example: async-list/reload** #### React ```tsx import { useAsyncList } from '@ark-ui/react/collection' interface Quote { id: number quote: string author: string } export const Reload = () => { const list = useAsyncList({ async load() { const response = await fetch(`https://dummyjson.com/quotes?limit=5&skip=${Math.floor(Math.random() * 50)}`) if (!response.ok) { throw new Error('Failed to fetch quotes') } const data = await response.json() return { items: data.quotes } }, }) return (
{list.error &&
Error: {list.error.message}
}
{list.items.map((quote) => (
"{quote.quote}"
— {quote.author}
))}
) } ``` #### Solid ```tsx import { useAsyncList } from '@ark-ui/solid/collection' import { For } from 'solid-js' interface Quote { id: number quote: string author: string } export const Reload = () => { const list = useAsyncList({ async load() { const response = await fetch(`https://dummyjson.com/quotes?limit=5&skip=${Math.floor(Math.random() * 50)}`) if (!response.ok) { throw new Error('Failed to fetch quotes') } const data = await response.json() return { items: data.quotes } }, }) return (
{list().error &&
Error: {list().error.message}
}
{(quote) => (
"{quote.quote}"
— {quote.author}
)}
) } ``` #### Vue ```vue ``` #### Svelte ```svelte
{#if list().error}
Error: {list().error.message}
{/if}
{#each list().items as quote}
"{quote.quote}"
— {quote.author}
{/each}
``` ### Infinite Loading Implement pagination by returning a cursor that indicates more data is available. **Example: async-list/infinite-loading** #### React ```tsx import { useAsyncList } from '@ark-ui/react/collection' interface Post { userId: number id: number title: string body: string } export const InfiniteLoading = () => { const list = useAsyncList({ autoReload: true, async load({ cursor }) { const page = cursor || 1 const limit = 10 const start = (page - 1) * limit const response = await fetch(`https://jsonplaceholder.typicode.com/posts?_start=${start}&_limit=${limit}`) if (!response.ok) { throw new Error('Failed to fetch posts') } const posts: Post[] = await response.json() const hasNextPage = posts.length === limit return { items: posts, cursor: hasNextPage ? page + 1 : undefined, } }, }) return (
Loaded {list.items.length} posts {list.cursor && ` (more available)`}
{list.cursor && ( )} {list.error &&
Error: {list.error.message}
}
{list.items.map((post, index) => (
{index + 1}: {post.title}
{post.body}
))}
) } ``` #### Solid ```tsx import { useAsyncList } from '@ark-ui/solid/collection' import { For } from 'solid-js' interface Post { userId: number id: number title: string body: string } export const InfiniteLoading = () => { const list = useAsyncList({ autoReload: true, async load({ cursor }) { const page = cursor || 1 const limit = 10 const start = (page - 1) * limit const response = await fetch(`https://jsonplaceholder.typicode.com/posts?_start=${start}&_limit=${limit}`) if (!response.ok) { throw new Error('Failed to fetch posts') } const posts: Post[] = await response.json() const hasNextPage = posts.length === limit return { items: posts, cursor: hasNextPage ? page + 1 : undefined, } }, }) return (
Loaded {list().items.length} posts {list().cursor && ` (more available)`}
{list().cursor && ( )} {list().error &&
Error: {list().error.message}
}
{(post, index) => (
{index() + 1}: {post.title}
{post.body}
)}
) } ``` #### Vue ```vue ``` #### Svelte ```svelte
Loaded {list().items.length} posts {#if list().cursor}(more available){/if}
{#if list().cursor} {/if} {#if list().error}
Error: {list().error.message}
{/if}
{#each list().items as post, index}
{index + 1}: {post.title}
{post.body}
{/each}
``` ### Filtering Filter data based on user input with automatic debouncing and loading states. **Example: async-list/filter** #### React ```tsx import { useAsyncList } from '@ark-ui/react/collection' interface User { id: number name: string email: string department: string role: string } export const Filter = () => { const list = useAsyncList({ initialItems: mockUsers.slice(0, 5), // Show first 5 users initially async load({ filterText }) { await delay(500) // Simulate network delay if (!filterText) { return { items: mockUsers.slice(0, 5) } } const filtered = mockUsers.filter( (user) => user.name.toLowerCase().includes(filterText.toLowerCase()) || user.email.toLowerCase().includes(filterText.toLowerCase()), ) return { items: filtered } }, }) return (
list.setFilterText(e.target.value)} /> {list.loading && Loading...}
{list.error &&
Error: {list.error.message}
}
{list.items.map((user) => (
{user.name}
{user.email}
{user.department} • {user.role}
))}
{list.items.length === 0 && !list.loading &&
No results found
}
) } const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) const mockUsers: User[] = [ { id: 1, name: 'Alice Johnson', email: 'alice@example.com', department: 'Engineering', role: 'Senior Developer' }, { id: 2, name: 'Bob Smith', email: 'bob@example.com', department: 'Marketing', role: 'Marketing Manager' }, { id: 3, name: 'Carol Davis', email: 'carol@example.com', department: 'Engineering', role: 'Frontend Developer' }, { id: 4, name: 'David Wilson', email: 'david@example.com', department: 'Sales', role: 'Sales Representative' }, { id: 5, name: 'Eva Brown', email: 'eva@example.com', department: 'Engineering', role: 'DevOps Engineer' }, { id: 6, name: 'Frank Miller', email: 'frank@example.com', department: 'Support', role: 'Customer Success' }, { id: 7, name: 'Grace Lee', email: 'grace@example.com', department: 'Marketing', role: 'Content Creator' }, { id: 8, name: 'Henry Taylor', email: 'henry@example.com', department: 'Engineering', role: 'Backend Developer' }, { id: 9, name: 'Ivy Anderson', email: 'ivy@example.com', department: 'Sales', role: 'Account Manager' }, { id: 10, name: 'Jack Thompson', email: 'jack@example.com', department: 'Support', role: 'Technical Support' }, { id: 11, name: 'Kate Martinez', email: 'kate@example.com', department: 'Marketing', role: 'Brand Manager' }, { id: 12, name: 'Liam Garcia', email: 'liam@example.com', department: 'Engineering', role: 'Full Stack Developer' }, { id: 13, name: 'Mia Rodriguez', email: 'mia@example.com', department: 'Sales', role: 'Sales Director' }, { id: 14, name: 'Noah Lopez', email: 'noah@example.com', department: 'Support', role: 'Support Manager' }, { id: 15, name: 'Olivia White', email: 'olivia@example.com', department: 'Engineering', role: 'UI Designer' }, { id: 16, name: 'Paul Harris', email: 'paul@example.com', department: 'Marketing', role: 'Digital Marketer' }, { id: 17, name: 'Quinn Clark', email: 'quinn@example.com', department: 'Engineering', role: 'Mobile Developer' }, { id: 18, name: 'Ruby Lewis', email: 'ruby@example.com', department: 'Sales', role: 'Business Development' }, { id: 19, name: 'Sam Young', email: 'sam@example.com', department: 'Support', role: 'Documentation Specialist' }, { id: 20, name: 'Tina Walker', email: 'tina@example.com', department: 'Marketing', role: 'Social Media Manager' }, ] ``` #### Solid ```tsx import { useAsyncList } from '@ark-ui/solid/collection' import { For } from 'solid-js' interface User { id: number name: string email: string department: string role: string } export const Filter = () => { const list = useAsyncList({ initialItems: mockUsers.slice(0, 5), async load({ filterText }: { filterText?: string }) { await delay(500) if (!filterText) { return { items: mockUsers.slice(0, 5) } } const filtered = mockUsers.filter( (user) => user.name.toLowerCase().includes(filterText.toLowerCase()) || user.email.toLowerCase().includes(filterText.toLowerCase()), ) return { items: filtered } }, }) return (
list().setFilterText(e.target.value)} /> {list().loading && Loading...}
{list().error &&
Error: {list().error.message}
}
{(user) => (
{user.name}
{user.email}
{user.department} • {user.role}
)}
{list().items.length === 0 && !list().loading &&
No results found
}
) } const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) const mockUsers: User[] = [ { id: 1, name: 'Alice Johnson', email: 'alice@example.com', department: 'Engineering', role: 'Senior Developer' }, { id: 2, name: 'Bob Smith', email: 'bob@example.com', department: 'Marketing', role: 'Marketing Manager' }, { id: 3, name: 'Carol Davis', email: 'carol@example.com', department: 'Engineering', role: 'Frontend Developer' }, { id: 4, name: 'David Wilson', email: 'david@example.com', department: 'Sales', role: 'Sales Representative' }, { id: 5, name: 'Eva Brown', email: 'eva@example.com', department: 'Engineering', role: 'DevOps Engineer' }, { id: 6, name: 'Frank Miller', email: 'frank@example.com', department: 'Support', role: 'Customer Success' }, { id: 7, name: 'Grace Lee', email: 'grace@example.com', department: 'Marketing', role: 'Content Creator' }, { id: 8, name: 'Henry Taylor', email: 'henry@example.com', department: 'Engineering', role: 'Backend Developer' }, { id: 9, name: 'Ivy Anderson', email: 'ivy@example.com', department: 'Sales', role: 'Account Manager' }, { id: 10, name: 'Jack Thompson', email: 'jack@example.com', department: 'Support', role: 'Technical Support' }, { id: 11, name: 'Kate Martinez', email: 'kate@example.com', department: 'Marketing', role: 'Brand Manager' }, { id: 12, name: 'Liam Garcia', email: 'liam@example.com', department: 'Engineering', role: 'Full Stack Developer' }, { id: 13, name: 'Mia Rodriguez', email: 'mia@example.com', department: 'Sales', role: 'Sales Director' }, { id: 14, name: 'Noah Lopez', email: 'noah@example.com', department: 'Support', role: 'Support Manager' }, { id: 15, name: 'Olivia White', email: 'olivia@example.com', department: 'Engineering', role: 'UI Designer' }, { id: 16, name: 'Paul Harris', email: 'paul@example.com', department: 'Marketing', role: 'Digital Marketer' }, { id: 17, name: 'Quinn Clark', email: 'quinn@example.com', department: 'Engineering', role: 'Mobile Developer' }, { id: 18, name: 'Ruby Lewis', email: 'ruby@example.com', department: 'Sales', role: 'Business Development' }, { id: 19, name: 'Sam Young', email: 'sam@example.com', department: 'Support', role: 'Documentation Specialist' }, { id: 20, name: 'Tina Walker', email: 'tina@example.com', department: 'Marketing', role: 'Social Media Manager' }, ] ``` #### Vue ```vue ``` #### Svelte ```svelte
list().setFilterText(e.currentTarget.value)} /> {#if list().loading} Loading... {/if}
{#if list().error}
Error: {list().error.message}
{/if}
{#each list().items as user}
{user.name}
{user.email}
{user.department} • {user.role}
{/each}
{#if list().items.length === 0 && !list().loading}
No results found
{/if}
``` ### Sorting (Client-side) Sort data on the client side after loading from the server. **Example: async-list/sort-client-side** #### React ```tsx import { useAsyncList } from '@ark-ui/react/collection' import { useCollator } from '@ark-ui/react/locale' interface User { id: number name: string username: string email: string phone: string website: string } export const SortClientSide = () => { const collator = useCollator() const list = useAsyncList({ autoReload: true, load: async () => { const response = await fetch('https://jsonplaceholder.typicode.com/users') const data = await response.json() return { items: data } }, sort({ items, descriptor }) { return { items: items.sort((a, b) => { const { column, direction } = descriptor let cmp = collator.compare(String(a[column]), String(b[column])) if (direction === 'descending') { cmp *= -1 } return cmp }), } }, }) const handleSort = (column: keyof User) => { const currentSort = list.sortDescriptor let direction: 'ascending' | 'descending' = 'ascending' if (currentSort?.column === column && currentSort.direction === 'ascending') { direction = 'descending' } list.sort({ column, direction }) } const getSortIcon = (column: keyof User) => { const current = list.sortDescriptor if (current?.column !== column) return '↕️' return current.direction === 'ascending' ? '↑' : '↓' } const descriptor = list.sortDescriptor return (
{list.loading &&
Loading...
} {list.error &&
Error: {list.error.message}
}
Sorted by: {descriptor ? `${descriptor.column} (${descriptor.direction})` : 'none'}
{[ { key: 'name', label: 'Name' }, { key: 'username', label: 'Username' }, { key: 'email', label: 'Email' }, { key: 'phone', label: 'Phone' }, { key: 'website', label: 'Website' }, ].map(({ key, label }) => ( ))} {list.items.map((user) => ( ))}
handleSort(key as keyof User)}> {label} {getSortIcon(key as keyof User)}
{user.name} {user.username} {user.email} {user.phone} {user.website}
) } ``` #### Solid ```tsx import { useAsyncList } from '@ark-ui/solid/collection' import { useCollator } from '@ark-ui/solid/locale' import { For } from 'solid-js' interface User { id: number name: string username: string email: string phone: string website: string } export const SortClientSide = () => { const collator = useCollator() const list = useAsyncList({ autoReload: true, load: async () => { const response = await fetch('https://jsonplaceholder.typicode.com/users') const data = await response.json() return { items: data } }, sort({ items, descriptor }) { return { items: items.sort((a, b) => { const { column, direction } = descriptor let cmp = collator().compare(String(a[column]), String(b[column])) if (direction === 'descending') { cmp *= -1 } return cmp }), } }, }) const handleSort = (column: keyof User) => { const currentSort = list().sortDescriptor let direction: 'ascending' | 'descending' = 'ascending' if (currentSort?.column === column && currentSort.direction === 'ascending') { direction = 'descending' } list().sort({ column, direction }) } const getSortIcon = (column: keyof User) => { const current = list().sortDescriptor if (current?.column !== column) return '↕️' return current.direction === 'ascending' ? '↑' : '↓' } const descriptor = () => list().sortDescriptor return (
{list().loading &&
Loading...
} {list().error &&
Error: {list().error.message}
}
Sorted by: {descriptor() ? `${descriptor()?.column} (${descriptor()?.direction})` : 'none'}
{({ key, label }) => ( )} {(user) => ( )}
handleSort(key as keyof User)}> {label} {getSortIcon(key as keyof User)}
{user.name} {user.username} {user.email} {user.phone} {user.website}
) } ``` #### Vue ```vue ``` #### Svelte ```svelte
{#if list().loading}
Loading...
{/if} {#if list().error}
Error: {list().error.message}
{/if}
Sorted by: {descriptor ? `${descriptor.column} (${descriptor.direction})` : 'none'}
{#each [{ key: 'name', label: 'Name' }, { key: 'username', label: 'Username' }, { key: 'email', label: 'Email' }, { key: 'phone', label: 'Phone' }, { key: 'website', label: 'Website' }] as { key, label }} {/each} {#each list().items as user} {/each}
handleSort(key as keyof User)}> {label} {getSortIcon(key as keyof User)}
{user.name} {user.username} {user.email} {user.phone} {user.website}
``` ### Sorting (Server-side) Send sort parameters to the server and reload data when sorting changes. **Example: async-list/sort-server-side** #### React ```tsx import { useAsyncList } from '@ark-ui/react/collection' interface Product { id: number title: string price: number description: string category: string image: string rating: { rate: number count: number } } export const SortServerSide = () => { const list = useAsyncList({ autoReload: true, async load({ sortDescriptor }) { const url = new URL('https://fakestoreapi.com/products') if (sortDescriptor) { const { direction } = sortDescriptor url.searchParams.set('sort', direction === 'ascending' ? 'asc' : 'desc') } const response = await fetch(url) const items = await response.json() return { items } }, }) const handleSort = (column: keyof Product) => { const currentSort = list.sortDescriptor let direction: 'ascending' | 'descending' = 'ascending' if (currentSort?.column === column && currentSort.direction === 'ascending') { direction = 'descending' } list.sort({ column, direction }) } const getSortIcon = (column: keyof Product) => { const desc = list.sortDescriptor if (desc?.column !== column) return '↕️' return desc.direction === 'ascending' ? '↑' : '↓' } return (
{list.loading &&
Loading...
} {list.error &&
Error: {list.error.message}
}
{list.items.map((product) => (
{product.title}
${product.price}
{product.category}
{product.rating.rate} ({product.rating.count} reviews)
))}
{list.sortDescriptor && (
Sorted by {list.sortDescriptor.column} ({list.sortDescriptor.direction})
)}
) } ``` #### Solid ```tsx import { useAsyncList } from '@ark-ui/solid/collection' import { For, Show } from 'solid-js' interface Product { id: number title: string price: number description: string category: string image: string rating: { rate: number count: number } } export const SortServerSide = () => { const list = useAsyncList({ autoReload: true, async load({ sortDescriptor }) { const url = new URL('https://fakestoreapi.com/products') if (sortDescriptor) { const { direction } = sortDescriptor url.searchParams.set('sort', direction === 'ascending' ? 'asc' : 'desc') } const response = await fetch(url) const items = await response.json() return { items } }, }) const handleSort = (column: keyof Product) => { const currentSort = list().sortDescriptor let direction: 'ascending' | 'descending' = 'ascending' if (currentSort?.column === column && currentSort.direction === 'ascending') { direction = 'descending' } list().sort({ column, direction }) } const getSortIcon = (column: keyof Product) => { const desc = list().sortDescriptor if (desc?.column !== column) return '↕️' return desc.direction === 'ascending' ? '↑' : '↓' } return (
Loading...
{(err) =>
Error: {err().message}
}
{(product) => (
{product.title}
${product.price}
{product.category}
{product.rating.rate} ({product.rating.count} reviews)
)}
{(desc) => (
Sorted by {desc().column} ({desc().direction})
)}
) } ``` #### Vue ```vue ``` #### Svelte ```svelte
{#if list().loading}
Loading...
{/if} {#if list().error}
Error: {list().error.message}
{/if}
{#each list().items as product}
{product.title}
${product.price}
{product.category}
{product.rating.rate} ({product.rating.count} reviews)
{/each}
{#if list().sortDescriptor}
Sorted by {list().sortDescriptor?.column} ({list().sortDescriptor?.direction})
{/if}
``` ### Dependencies Automatically reload data when dependencies change, such as filter selections or external state. **Example: async-list/dependencies** #### React ```tsx import { useAsyncList } from '@ark-ui/react/collection' import { useState } from 'react' interface User { id: number name: string email: string department: string role: string } export const Dependencies = () => { const [selectedDepartment, setSelectedDepartment] = useState('') const [selectedRole, setSelectedRole] = useState('') const list = useAsyncList({ initialItems: mockUsers, // Show all users initially dependencies: [selectedDepartment, selectedRole], async load({ filterText }) { await delay(400) // Simulate network delay let items = mockUsers // Filter by department if (selectedDepartment) { items = items.filter((user) => user.department === selectedDepartment) } // Filter by role if (selectedRole) { items = items.filter((user) => user.role === selectedRole) } // Filter by search text if (filterText) { items = items.filter( (user) => user.name.toLowerCase().includes(filterText.toLowerCase()) || user.email.toLowerCase().includes(filterText.toLowerCase()), ) } return { items } }, }) return (

Dependencies Example

list.setFilterText(e.target.value)} /> {list.loading && Loading...}
{list.error &&
Error: {list.error.message}
}
Found {list.items.length} users
{list.items.map((user) => (
{user.name}
{user.email}
{user.department} • {user.role}
))}
{list.items.length === 0 && !list.loading &&
No users found with current filters
}
) } // Helper function to simulate API delay const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) const departments = ['Engineering', 'Marketing', 'Sales', 'Support'] const roles = [ 'Senior Developer', 'Marketing Manager', 'Frontend Developer', 'Sales Representative', 'DevOps Engineer', 'Customer Success', 'Content Creator', 'Backend Developer', 'Account Manager', 'Technical Support', 'Brand Manager', 'Full Stack Developer', 'Sales Director', 'Support Manager', 'UI Designer', 'Digital Marketer', 'Mobile Developer', 'Business Development', 'Documentation Specialist', 'Social Media Manager', ] const mockUsers: User[] = [ { id: 1, name: 'Alice Johnson', email: 'alice@example.com', department: 'Engineering', role: 'Senior Developer' }, { id: 2, name: 'Bob Smith', email: 'bob@example.com', department: 'Marketing', role: 'Marketing Manager' }, { id: 3, name: 'Carol Davis', email: 'carol@example.com', department: 'Engineering', role: 'Frontend Developer' }, { id: 4, name: 'David Wilson', email: 'david@example.com', department: 'Sales', role: 'Sales Representative' }, { id: 5, name: 'Eva Brown', email: 'eva@example.com', department: 'Engineering', role: 'DevOps Engineer' }, { id: 6, name: 'Frank Miller', email: 'frank@example.com', department: 'Support', role: 'Customer Success' }, { id: 7, name: 'Grace Lee', email: 'grace@example.com', department: 'Marketing', role: 'Content Creator' }, { id: 8, name: 'Henry Taylor', email: 'henry@example.com', department: 'Engineering', role: 'Backend Developer' }, { id: 9, name: 'Ivy Anderson', email: 'ivy@example.com', department: 'Sales', role: 'Account Manager' }, { id: 10, name: 'Jack Thompson', email: 'jack@example.com', department: 'Support', role: 'Technical Support' }, { id: 11, name: 'Kate Martinez', email: 'kate@example.com', department: 'Marketing', role: 'Brand Manager' }, { id: 12, name: 'Liam Garcia', email: 'liam@example.com', department: 'Engineering', role: 'Full Stack Developer' }, { id: 13, name: 'Mia Rodriguez', email: 'mia@example.com', department: 'Sales', role: 'Sales Director' }, { id: 14, name: 'Noah Lopez', email: 'noah@example.com', department: 'Support', role: 'Support Manager' }, { id: 15, name: 'Olivia White', email: 'olivia@example.com', department: 'Engineering', role: 'UI Designer' }, { id: 16, name: 'Paul Harris', email: 'paul@example.com', department: 'Marketing', role: 'Digital Marketer' }, { id: 17, name: 'Quinn Clark', email: 'quinn@example.com', department: 'Engineering', role: 'Mobile Developer' }, { id: 18, name: 'Ruby Lewis', email: 'ruby@example.com', department: 'Sales', role: 'Business Development' }, { id: 19, name: 'Sam Young', email: 'sam@example.com', department: 'Support', role: 'Documentation Specialist' }, { id: 20, name: 'Tina Walker', email: 'tina@example.com', department: 'Marketing', role: 'Social Media Manager' }, ] ``` #### Solid ```tsx import { useAsyncList } from '@ark-ui/solid/collection' import { createSignal, For } from 'solid-js' interface User { id: number name: string email: string department: string role: string } export const Dependencies = () => { const [selectedDepartment, setSelectedDepartment] = createSignal('') const [selectedRole, setSelectedRole] = createSignal('') const list = useAsyncList({ initialItems: mockUsers, get dependencies() { return [selectedDepartment(), selectedRole()] }, async load({ filterText }: { filterText?: string }) { await delay(400) let items = mockUsers if (selectedDepartment()) { items = items.filter((user) => user.department === selectedDepartment()) } if (selectedRole()) { items = items.filter((user) => user.role === selectedRole()) } if (filterText) { items = items.filter( (user) => user.name.toLowerCase().includes(filterText.toLowerCase()) || user.email.toLowerCase().includes(filterText.toLowerCase()), ) } return { items } }, }) return (

Dependencies Example

list().setFilterText(e.target.value)} /> {list().loading && Loading...}
{list().error &&
Error: {list().error.message}
}
Found {list().items.length} users
{(user) => (
{user.name}
{user.email}
{user.department} • {user.role}
)}
{list().items.length === 0 && !list().loading &&
No users found with current filters
}
) } const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) const departments = ['Engineering', 'Marketing', 'Sales', 'Support'] const roles = [ 'Senior Developer', 'Marketing Manager', 'Frontend Developer', 'Sales Representative', 'DevOps Engineer', 'Customer Success', 'Content Creator', 'Backend Developer', 'Account Manager', 'Technical Support', 'Brand Manager', 'Full Stack Developer', 'Sales Director', 'Support Manager', 'UI Designer', 'Digital Marketer', 'Mobile Developer', 'Business Development', 'Documentation Specialist', 'Social Media Manager', ] const mockUsers: User[] = [ { id: 1, name: 'Alice Johnson', email: 'alice@example.com', department: 'Engineering', role: 'Senior Developer' }, { id: 2, name: 'Bob Smith', email: 'bob@example.com', department: 'Marketing', role: 'Marketing Manager' }, { id: 3, name: 'Carol Davis', email: 'carol@example.com', department: 'Engineering', role: 'Frontend Developer' }, { id: 4, name: 'David Wilson', email: 'david@example.com', department: 'Sales', role: 'Sales Representative' }, { id: 5, name: 'Eva Brown', email: 'eva@example.com', department: 'Engineering', role: 'DevOps Engineer' }, { id: 6, name: 'Frank Miller', email: 'frank@example.com', department: 'Support', role: 'Customer Success' }, { id: 7, name: 'Grace Lee', email: 'grace@example.com', department: 'Marketing', role: 'Content Creator' }, { id: 8, name: 'Henry Taylor', email: 'henry@example.com', department: 'Engineering', role: 'Backend Developer' }, { id: 9, name: 'Ivy Anderson', email: 'ivy@example.com', department: 'Sales', role: 'Account Manager' }, { id: 10, name: 'Jack Thompson', email: 'jack@example.com', department: 'Support', role: 'Technical Support' }, { id: 11, name: 'Kate Martinez', email: 'kate@example.com', department: 'Marketing', role: 'Brand Manager' }, { id: 12, name: 'Liam Garcia', email: 'liam@example.com', department: 'Engineering', role: 'Full Stack Developer' }, { id: 13, name: 'Mia Rodriguez', email: 'mia@example.com', department: 'Sales', role: 'Sales Director' }, { id: 14, name: 'Noah Lopez', email: 'noah@example.com', department: 'Support', role: 'Support Manager' }, { id: 15, name: 'Olivia White', email: 'olivia@example.com', department: 'Engineering', role: 'UI Designer' }, { id: 16, name: 'Paul Harris', email: 'paul@example.com', department: 'Marketing', role: 'Digital Marketer' }, { id: 17, name: 'Quinn Clark', email: 'quinn@example.com', department: 'Engineering', role: 'Mobile Developer' }, { id: 18, name: 'Ruby Lewis', email: 'ruby@example.com', department: 'Sales', role: 'Business Development' }, { id: 19, name: 'Sam Young', email: 'sam@example.com', department: 'Support', role: 'Documentation Specialist' }, { id: 20, name: 'Tina Walker', email: 'tina@example.com', department: 'Marketing', role: 'Social Media Manager' }, ] ``` #### Vue ```vue ``` #### Svelte ```svelte
list().setFilterText(e.currentTarget.value)} /> {#if list().loading} Loading... {/if}
{#if list().error}
Error: {list().error.message}
{/if}
Found {list().items.length} users
{#each list().items as user}
{user.name}
{user.email}
{user.department} • {user.role}
{/each}
{#if list().items.length === 0 && !list().loading}
No users found with current filters
{/if}
``` ## API Reference ### Props - **load** (`(params: LoadParams) => Promise>`) - Function to load data asynchronously - **sort** (`(params: SortParams) => Promise> | SortResult`) - Optional function for client-side sorting - **autoReload** (`boolean`, default: `false`) - Whether to automatically reload data on mount - **initialItems** (`T[]`, default: `[]`) - Initial items to display before first load - **dependencies** (`any[]`, default: `[]`) - Values that trigger a reload when changed - **initialFilterText** (`string`, default: `''`) - Initial filter text value - **initialSortDescriptor** (`SortDescriptor | null`) - Initial sort configuration ### Load Parameters The `load` function receives an object with the following properties: - **cursor** (`C | undefined`) - Current cursor for pagination - **filterText** (`string`) - Current filter text - **sortDescriptor** (`SortDescriptor | null`) - Current sort configuration - **signal** (`AbortSignal`) - AbortController signal for request cancellation ### Load Result The `load` function should return an object with: - **items** (`T[]`) - The loaded items - **cursor** (`C | undefined`) - Optional cursor for next page ### Sort Parameters The `sort` function receives an object with: - **items** (`T[]`) - Current items to sort - **descriptor** (`SortDescriptor`) - Sort configuration with `column` and `direction` ### Return Value The hook returns an object with the following properties and methods: #### State Properties - **items** (`T[]`) - Current list of items - **loading** (`boolean`) - Whether a load operation is in progress - **error** (`Error | null`) - Any error from the last operation - **cursor** (`C | undefined`) - Current cursor for pagination - **filterText** (`string`) - Current filter text - **sortDescriptor** (`SortDescriptor | null`) - Current sort configuration #### Methods - **reload** (`() => void`) - Reload data from the beginning - **loadMore** (`() => void`) - Load more items (when cursor is available) - **setFilterText** (`(text: string) => void`) - Set filter text and reload - **sort** (`(descriptor: SortDescriptor) => void`) - Apply sorting #### Types ```tsx interface SortDescriptor { column: string direction: 'ascending' | 'descending' } interface LoadParams { cursor?: C filterText: string sortDescriptor?: SortDescriptor | null signal: AbortSignal } interface LoadResult { items: T[] cursor?: C } ```