> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# useInfiniteScroll

> A hook for implementing infinite scroll loading behavior using Intersection Observer.

A hook for implementing infinite scroll loading behavior using Intersection Observer.

## Usage

```tsx theme={null}
import { useInfiniteScroll } from "@servicetitan/anvil2-ext-atlas";

function InfiniteList() {
  const [items, setItems] = useState([]);
  const [hasMore, setHasMore] = useState(true);
  const [isLoading, setIsLoading] = useState(false);

  const loadMore = async () => {
    setIsLoading(true);
    const newItems = await fetchItems();
    setItems((prev) => [...prev, ...newItems]);
    setHasMore(newItems.length > 0);
    setIsLoading(false);
  };

  const { sentinelRef } = useInfiniteScroll({
    hasMore,
    isLoading,
    onLoadMore: loadMore,
    rootMargin: "100px",
    threshold: 0.1,
  });

  return (
    <div>
      {items.map((item) => (
        <Item key={item.id} {...item} />
      ))}
      {hasMore && <div ref={sentinelRef} />}
    </div>
  );
}
```

## Options

| Option     | Type       | Default | Description                                         |
| ---------- | ---------- | ------- | --------------------------------------------------- |
| hasMore    | boolean    | -       | Whether there is more content to load               |
| isLoading  | boolean    | -       | Whether content is currently being loaded           |
| onLoadMore | () => void | -       | Callback to load more content                       |
| rootMargin | string     | "100px" | Margin around the root for intersection calculation |
| threshold  | number     | 0.1     | Visibility threshold to trigger loading             |

## Returns

| Property    | Type                       | Description                           |
| ----------- | -------------------------- | ------------------------------------- |
| sentinelRef | RefObject\<HTMLDivElement> | Ref to attach to the sentinel element |

## Behavior

* **Intersection Observer**: Uses native browser API for efficient scroll detection
* **Load Gating**: Only triggers `onLoadMore` when `hasMore` is true and `isLoading` is false
* **Cleanup**: Properly disconnects observer on unmount or option changes

## Example: With InfiniteContent Component

The `InfiniteContent` component uses this hook internally, but you can use the hook directly for custom implementations:

```tsx theme={null}
import { useInfiniteScroll, Spinner } from "@servicetitan/anvil2-ext-atlas";

function CustomInfiniteScroll() {
  const [messages, setMessages] = useState([]);
  const [hasMore, setHasMore] = useState(true);
  const [loading, setLoading] = useState(false);

  const { sentinelRef } = useInfiniteScroll({
    hasMore,
    isLoading: loading,
    onLoadMore: async () => {
      setLoading(true);
      const older = await fetchOlderMessages();
      setMessages((prev) => [...older, ...prev]);
      setHasMore(older.length === 20);
      setLoading(false);
    },
    rootMargin: "200px",
  });

  return (
    <div className="message-list">
      {hasMore && <div ref={sentinelRef}>{loading && <Spinner />}</div>}
      {messages.map((msg) => (
        <Message key={msg.id} {...msg} />
      ))}
    </div>
  );
}
```
