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

# Infinite scroll with React InstantSearch

> Implement an infinite scroll experience with React InstantSearch.

export const Index = () => <Tooltip tip="An Algolia index is a searchable dataset that consists of records and configuration settings. These settings define how the records are searched and ranked.">
    index
  </Tooltip>;

<Note>
  This is the **React InstantSearch v7** documentation.
  If you're upgrading from v6, see the [upgrade guide](/doc/guides/building-search-ui/upgrade-guides/react/#migrate-from-react-instantsearch-v6-to-react-instantsearch-v7).
  If you were using React InstantSearch Hooks,
  this v7 documentation applies—just check for [necessary changes](/doc/guides/building-search-ui/upgrade-guides/react/#migrate-from-react-instantsearch-hooks-to-react-instantsearch-v7).
  To continue using v6, you can find the [archived documentation](https://algolia.com/old-docs/deprecated/instantsearch/react/v6/api-reference/instantsearch/).
</Note>

An *infinite list* is a common way of displaying results.
It's especially well-suited to mobile devices and has two variants:

* **Infinite hits** with a "See more" button at the end of a batch of results. Implement this with InstantSearch's [`InfiniteHits`](/doc/api-reference/widgets/infinite-hits/react) widget.
* **Infinite scroll** uses a listener on the scroll event (called when users have scrolled to the end of the first batch of results). The following guidance implements such an infinite scroll.

<Columns>
  <Card title="Open CodeSandbox" icon="codesandbox" href="https://codesandbox.io/s/github/algolia/doc-code-samples/tree/master/react-instantsearch/infinite-scroll">
    Run and edit the Infinite scroll with React InstantSearch example in CodeSandbox.
  </Card>

  <Card title="Explore source code" icon="github" href="https://github.com/algolia/doc-code-samples/tree/master/react-instantsearch/infinite-scroll">
    Browse the source for the Infinite scroll with React InstantSearch example on GitHub.
  </Card>
</Columns>

<Note>
  If there are no hits, you should [display a message to users and clear filters](/doc/guides/building-search-ui/going-further/conditional-display/react#handling-no-results) so they can start over.
</Note>

## Display a list of hits

Render the results with the [`useInfiniteHits`](/doc/api-reference/widgets/infinite-hits/react#hook) Hook.

```jsx React icon=code theme={"system"}
import React from "react";
import { Highlight, useInfiniteHits, Snippet } from "react-instantsearch";

export function InfiniteHits(props) {
  const { items } = useInfiniteHits(props);

  return (
    <div className="ais-InfiniteHits">
      <ul className="ais-InfiniteHits-list">
        {items.map((hit) => (
          <li key={hit.objectID} className="ais-InfiniteHits-item">
            <article>
              <h2>
                <Highlight attribute="name" hit={hit} />
              </h2>
              <p>
                <Snippet attribute="description" hit={hit} />
              </p>
            </article>
          </li>
        ))}
      </ul>
    </div>
  );
}
```

## Track the scroll position

Once you have your results, the next step is to track the scroll position to determine when the rest of the content needs to be loaded (using the Intersection Observer API). Use the API to track when the bottom of the list (the "sentinel" element) enters the viewport. You can reuse the same element across different renders.

```jsx React icon=code theme={"system"}
import React, { useRef } from "react";
// ...

export function InfiniteHits(props) {
  const { items } = useInfiniteHits(props);
  const sentinelRef = useRef(null);

  return (
    <div className="ais-InfiniteHits">
      <ul className="ais-InfiniteHits-list">
        {/* ... */}
        <li ref={sentinelRef} aria-hidden="true" />
      </ul>
    </div>
  );
}
```

<Note>
  This implementation uses the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
  To support [Internet Explorer 11](https://wikipedia.org/wiki/Internet_Explorer_11) you need a [polyfill](https://wikipedia.org/wiki/Polyfill_\(programming\)) for
  `IntersectionObserver`. A browser API is used in the example, but you can apply the concepts to any infinite scroll library.
</Note>

Now create an Intersection Observer instance to observe when the sentinel element enters the page.
Disconnect the observer in the effect's cleanup.

```jsx React icon=code theme={"system"}
import React, { useEffect, useRef } from "react";
// ...

export function InfiniteHits(props) {
  const { items, isLastPage, showMore } = useInfiniteHits(props);
  const sentinelRef = useRef(null);

  useEffect(() => {
    if (sentinelRef.current !== null) {
      const observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting && !isLastPage) {
            // Load more hits
          }
        });
      });

      observer.observe(sentinelRef.current);

      return () => {
        observer.disconnect();
      };
    }
  }, [isLastPage, showMore]);

  return (
    <div className="ais-InfiniteHits">
      <ul className="ais-InfiniteHits-list">
        {/* ... */}
        <li ref={sentinelRef} aria-hidden="true" />
      </ul>
    </div>
  );
}
```

## Retrieve more results

Now that you can track when you reach the bottom of the list,
call the [`showMore`](/doc/api-reference/widgets/infinite-hits/react#param-show-more) function inside the observer's callback.
The Hook exposes whether there are more results to load with [`isLastPage`](/doc/api-reference/widgets/infinite-hits/react#param-is-last-page), allowing you to conditionally call [`showMore`](/doc/api-reference/widgets/infinite-hits/react#param-show-more).

```jsx React icon=code theme={"system"}
// ...

export function InfiniteHits(props) {
  const { items, isLastPage, showMore } = useInfiniteHits(props);
  const sentinelRef = useRef(null);

  useEffect(() => {
    if (sentinelRef.current !== null) {
      const observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting && !isLastPage) {
            showMore();
          }
        });
      });

      observer.observe(sentinelRef.current);

      return () => {
        observer.disconnect();
      };
    }
  }, [isLastPage, showMore]);

  // ...
}
```

## Show more than 1,000 hits

To ensure excellent performance, the default limit for the number of hits you can retrieve for a query is
1,000.

<CodeGroup>
  ```cs C# theme={"system"}
  var response = await client.SetSettingsAsync(
    "INDEX_NAME",
    new IndexSettings { PaginationLimitedTo = 1000 }
  );
  ```

  ```dart Dart theme={"system"}
  final response = await client.setSettings(
    indexName: "INDEX_NAME",
    indexSettings: IndexSettings(
      paginationLimitedTo: 1000,
    ),
  );
  ```

  ```go Go theme={"system"}
  response, err := client.SetSettings(client.NewApiSetSettingsRequest(
    "INDEX_NAME",
    search.NewEmptyIndexSettings().SetPaginationLimitedTo(1000)))
  if err != nil {
    // handle the eventual error
    panic(err)
  }
  ```

  ```java Java theme={"system"}
  UpdatedAtResponse response = client.setSettings("INDEX_NAME", new IndexSettings().setPaginationLimitedTo(1000));
  ```

  ```js JavaScript theme={"system"}
  const response = await client.setSettings({
    indexName: 'theIndexName',
    indexSettings: { paginationLimitedTo: 1000 },
  });
  ```

  ```kotlin Kotlin theme={"system"}
  var response =
    client.setSettings(
      indexName = "INDEX_NAME",
      indexSettings = IndexSettings(paginationLimitedTo = 1000),
    )
  ```

  ```php PHP theme={"system"}
  $response = $client->setSettings(
      'INDEX_NAME',
      ['paginationLimitedTo' => 1000,
      ],
  );
  ```

  ```python Python theme={"system"}
  response = client.set_settings(
      index_name="INDEX_NAME",
      index_settings={
          "paginationLimitedTo": 1000,
      },
  )
  ```

  ```ruby Ruby theme={"system"}
  response = client.set_settings("INDEX_NAME", Algolia::Search::IndexSettings.new(pagination_limited_to: 1000))
  ```

  ```scala Scala theme={"system"}
  val response = Await.result(
    client.setSettings(
      indexName = "INDEX_NAME",
      indexSettings = IndexSettings(
        paginationLimitedTo = Some(1000)
      )
    ),
    Duration(100, "sec")
  )
  ```

  ```swift Swift theme={"system"}
  let response = try await client.setSettings(
      indexName: "INDEX_NAME",
      indexSettings: IndexSettings(paginationLimitedTo: 1000)
  )
  ```
</CodeGroup>

If you need to show more than 1,000 hits, you can set a different limit using the [`paginationLimitedTo`](/doc/api-reference/api-parameters/paginationLimitedTo) parameter.
The higher you set this limit, the slower your search performance can become.

<Note>
  Increasing the limit doesn't mean you can go until the end of the hits,
  but just that Algolia will go as far as possible in the <Index /> to retrieve results in a reasonable time.
</Note>

## Next steps

You now have a good starting point to create an even richer experience with React InstantSearch. Next, you could improve this app by:

* [Building a native search UI with React Native](/doc/guides/building-search-ui/going-further/native/react).
* Checking the [API reference](/doc/api-reference/widgets/react) to discover more Hooks.
