Socket
Book a DemoInstallSign in
Socket

@vouill/react-infinite-scroll

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vouill/react-infinite-scroll

Dead simple infinite scroll component for React.

latest
Source
npmnpm
Version
1.2.0
Version published
Maintainers
1
Created
Source

npm bundle size NPM License NPM Version

@vouill/react-infinite-scroll

A very simple and opinionated way to handle infinite scrolling with react.

Uses the Intersection Observer API and is SSR friendly.

Have a look at the code and feel free to take it directly if needed.

Usage

import { LoadMore } from "@vouill/react-infinite-scroll"

...
    <YourList/>
    <LoadMore onLoadMore={loadMoreData}/>
...

When this component is reached while scrolling or rendering, it will fire the onLoadMore event.

You can place this component at the end of an infinite list, or use a bit of CSS and place it at specific scroll percentage.

If you want to have more granular control over it, simply add your conditions when rendering it:

{
  !isLoading && hasNextPage && <LoadMore onLoadMore={loadMoreData} />
}

Run this repo and it's examples:

Open in StackBlitz

Examples

Simple synchronous setup

Codeblitz

export const SynchronousData = () => {
  const [data, setData] = useState<string[]>(fakeData)
  return (
    <div className="App">
      <div>
        {data.map((content, index) => (
          <div key={index}>
            {content} - {index}
          </div>
        ))}
        <LoadMore
          onLoadMore={() => {
            return setData((d) => [...d, ...fakeData])
          }}
        />
      </div>
    </div>
  )
}

Real life api setup & load when 80% scrolled

With react query

Try live on Codeblitz

import { LoadMore } from "@vouill/react-infinite-scroll"
import { useInfiniteQuery } from "@tanstack/react-query"

const LIMIT = 30

const getUrlOffset = (url: string) => {
  if (!url) return null
  const regex = /[?&]offset=(\d+)/
  const match = url.match(regex)
  const offset = match ? match[1] : null
  return offset
}

export default function App() {
  const { data, isFetching, fetchNextPage, hasNextPage } = useInfiniteQuery<{
    results: { name: string }[]
    count: number
    next: string
  }>({
    queryKey: ["pokemons"],
    queryFn: async ({ pageParam }) =>
      (
        await fetch(
          `https://pokeapi.co/api/v2/ability/?limit=${LIMIT}&offset=${pageParam}`
        )
      ).json(),
    initialPageParam: 0,
    getNextPageParam: (lastPage) => getUrlOffset(lastPage.next),
  })

  return (
    <div className="App">
      <p>
        This list uses the{" "}
        <a href="https://pokeapi.co/docs/v2#resource-listspagination-section">
          pokeapi
        </a>
      </p>
      <div style={{ position: "relative" }}>
        {data?.pages?.map(({ results }, pageIndex) =>
          results.map(({ name }, resultIndex) => (
            <div key={name}>
              {pageIndex * LIMIT + resultIndex} - {name}
            </div>
          ))
        )}
        {isFetching && "loading..."}
        {!isFetching && hasNextPage && (
          <LoadMore
            style={{ position: "absolute", bottom: "20%" }}
            onLoadMore={() => {
              fetchNextPage()
            }}
          />
        )}
      </div>
    </div>
  )
}

Simple fetch and useState

Try live on Codeblitz

export const SynchronousData = () => {
  const [count, setCount] = useState(0)
  const [nextPage, setNextPage] = useState("")
  const [data, setData] = useState<{ name: string }[]>([])

  useEffect(() => {
    const fetchData = async () => {
      const { results, next, count } = await (
        await fetch("https://pokeapi.co/api/v2/ability/?limit=40&offset=20")
      ).json()
      setCount(count)
      setNextPage(next)
      setData(results)
    }
    fetchData()
  }, [])
  return (
    <div className="App">
      <p>
        This list uses the{" "}
        <a href="https://pokeapi.co/docs/v2#resource-listspagination-section">
          pokeapi
        </a>
      </p>
      <div style={{ position: "relative" }}>
        {data.map(({ name }, index) => (
          <div key={index}>{name}</div>
        ))}
        {count > data.length && (
          <LoadMore
            style={{ position: "absolute", bottom: "20%" }}
            onLoadMore={async () => {
              if (nextPage) {
                const {
                  results = [],
                  next,
                  count,
                } = await (await fetch(nextPage)).json()
                setCount(count)
                setNextPage(next)
                setData((d) => [...d, ...results])
              }
            }}
          />
        )}
      </div>
    </div>
  )
}

API

  • onLoadMore: Event fired when the component is "visible" in the current view.
  • as: redefine the rendered component, by default it's an empty div.
  • ...props: you can forward any props if needed.

Keywords

react

FAQs

Package last updated on 07 Apr 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts