{"slug":"async-list","title":"Async List","description":"Using the Async List machine in your project.","contentType":"utilities","content":"The async list is used to display a list of items that are loaded\nasynchronously. Usually paired with the combobox or select component.\n\n> This was inspired by React Stately's\n> [useAsyncList](https://react-spectrum.adobe.com/react-stately/useAsyncList.html)\n> hook.\n\n<Resources pkg=\"@zag-js/async-list\" />\n\n**Features**\n\n- Support for pagination, sorting, and filtering\n- Support for abortable requests via `AbortSignal`\n- Manages loading and error states\n\n## Installation\n\nTo use the Async List machine in your project, run the following command in your\ncommand line:\n\n<CodeSnippet id=\"async-list/installation.mdx\" />\n\n## Usage\n\nFirst, import the async list package into your project\n\n```jsx\nimport * as asyncList from \"@zag-js/async-list\"\n```\n\nThe async list package exports two key functions:\n\n- `machine` — The state machine logic for the async list widget.\n- `connect` — returns the properties and methods for the async data management.\n\n<CodeSnippet id=\"async-list/usage.mdx\" />\n\n### Loading and Error States\n\nThe async list machine will automatically return the `error` and `loading`\nproperties in the `api` when the `load` function returns an error or is loading.\n\n- `api.error` — The error instance returned by the last fetch.\n- `api.loading` — Whether the list is loading.\n- `api.items` — The items in the list after the last fetch.\n\n### Pagination\n\nThe async list supports paginated data to avoid loading too many items at once.\nThis is accomplished by returning a cursor in addition to items from the `load`\nfunction.\n\nWhen `loadMore` is called, the cursor is passed back to your `load` function,\nwhich you can use to determine the URL for the next page.\n\n```tsx\nconst service = useMachine(asyncList.machine, {\n  async load({ signal, cursor }) {\n    const requestUrl = cursor || \"https://pokeapi.co/api/v2/pokemon\"\n    const res = await fetch(requestUrl, { signal })\n    const json = await res.json()\n\n    return {\n      items: json.results,\n      cursor: json.next,\n    }\n  },\n})\n```\n\nThen, you can use the `api.loadMore` method to load more items.\n\n```tsx\napi.loadMore()\n```\n\n### Reloading the data\n\nUse the `api.reload` method to reload the data.\n\n```tsx\napi.reload()\n```\n\n### Sorting\n\nThe async list machine supports both client-side and server-side sorting. You\ncan implement sorting by providing a `sort` function for client-side operations,\nor by handling the `sortDescriptor` parameter in your `load` function for\nserver-side sorting.\n\nRegardless of the sorting implementation, the way to trigger the sorting is by\ncalling the `api.sort` method with the descriptor.\n\n```tsx\napi.sort({ column: \"name\", direction: \"ascending\" })\n```\n\n#### Client-side sorting\n\nUse the `sort` function to implement client-side sorting.\n\n```tsx\nconst service = useMachine(asyncList.machine, {\n  async load({ signal }) {\n    // ...\n  },\n  sort({ items, sortDescriptor }) {\n    return {\n      items: items.sort((a, b) => {\n        // Compare the items by the sorted column\n        const aColumn = a[sortDescriptor.column]\n        const bColumn = b[sortDescriptor.column]\n        let direction = aColumn.localeCompare(bColumn)\n\n        // Flip the direction if descending order is specified.\n        if (sortDescriptor.direction === \"descending\") {\n          direction *= -1\n        }\n\n        return direction\n      }),\n    }\n  },\n})\n```\n\n#### Server-side sorting\n\nFor server-side sorting, use the `sortDescriptor` parameter in the `load`\nfunction to pass sorting parameters to your API.\n\n```tsx\nconst service = useMachine(asyncList.machine, {\n  async load({ signal, sortDescriptor }) {\n    let url = new URL(\"http://example.com/api\")\n    if (sortDescriptor) {\n      url.searchParams.append(\"sort_key\", sortDescriptor.column)\n      url.searchParams.append(\"sort_direction\", sortDescriptor.direction)\n    }\n\n    let res = await fetch(url, { signal })\n    let json = await res.json()\n    return {\n      items: json.results,\n    }\n  },\n})\n```\n\n### Filtering\n\nFiltering your data list is often necessary, such as for user searches or query\nlookups. For server-side filtering, use the `filterText` parameter in the `load`\nfunction.\n\nThe way to trigger the filtering is by calling the `api.setFilterText` method\nwith the filter text.\n\n```tsx\napi.setFilterText(\"filter text\")\n```\n\nThe `api.setFilterText` method modifies the `filterText` and calls the `load`\nfunction to refresh the data with the updated filter.\n\n```tsx\nconst service = useMachine(asyncList.machine, {\n  async load({ signal, filterText }) {\n    let url = new URL(\"http://example.com/api\")\n    if (filterText) {\n      url.searchParams.append(\"filter\", filterText)\n    }\n\n    let res = await fetch(url, { signal })\n    let json = await res.json()\n    return {\n      items: json.results,\n    }\n  },\n})\n```\n\n### Aborting requests\n\nUse the `api.abort` method to abort the current request.\n\n```tsx\napi.abort()\n```\n\nThis only works if you pass the `signal` parameter to the `load` function.\n\n```tsx\nconst service = useMachine(asyncList.machine, {\n  async load({ signal }) {\n    // ...\n  },\n})\n```\n\n### Registering external dependencies\n\nIn even more complex scenarios, you may need to register external dependencies\nthat trigger a reload of the data.\n\nUse the `dependencies` parameter to register external dependencies. Ensure every\ndependency is a primitive value (no objects, arrays, maps, sets, etc.).\n\n```tsx\nconst service = useMachine(asyncList.machine, {\n  dependencies: [\"user\"],\n  async load({ signal, deps }) {\n    // ...\n  },\n})\n```\n\n## Methods and Properties\n\nThe async list's `api` exposes the following methods and properties:\n\n### Machine Context\n\nThe async list machine exposes the following context properties:\n\n<ContextTable name=\"async-list\" />\n\n### Machine API\n\nThe async list `api` exposes the following methods:\n\n<ApiTable name=\"async-list\" />","package":"@zag-js/async-list","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/utilities/async-list.mdx"}