bigbullui logobigbullui
Componentnavigation

InfiniteScroll

IntersectionObserver sentinel: when visible calls onLoadMore, shows animated loader row; wraps children.

Preview

Live Preview
Item 1
Item 2
Item 3
Item 4
Item 5
Loading more items...

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add infinite-scroll

Or install the full npm package:

npm install bigbullui

Usage

import { InfiniteScroll } from "@/components/ui/infinite-scroll"

<InfiniteScroll onLoadMore={() => {}} hasMore={true}>
  {[1, 2, 3, 4, 5].map((i) => <div key={i} className="p-3 bg-card">Item {i}</div>)}
</InfiniteScroll>

Source code

infinite-scroll.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

import * as React from "react";
import { cn } from "./lib/utils";

export type InfiniteScrollProps = {
  onLoadMore: () => void;
  hasMore: boolean;
  loader?: React.ReactNode;
  children?: React.ReactNode;
};

export function InfiniteScroll({ onLoadMore, hasMore, loader, children }: InfiniteScrollProps) {

  const sentinelRef = React.useRef<HTMLDivElement>(null);

  React.useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting) {
          onLoadMore();
        }
      },
      { rootMargin: "100px" },
    );

    if (sentinelRef.current) {
      observer.observe(sentinelRef.current);
    }

    return () => observer.disconnect();
  }, [onLoadMore]);

  return (
    <div className="relative">
      {children}
      {hasMore && (
        <div
          ref={sentinelRef}
          className={cn(
            "py-6 text-center",
            "motion-reduce:animate-none",
          )}
        >
          {loader || (
            <span className="text-sm font-mono uppercase tracking-[0.15em] text-muted-foreground">
              Loading more...
            </span>
          )}
        </div>
      )}
    </div>
  );
}

Props

PropTypeDescription
onLoadMore() => voidCallback when sentinel intersects.
hasMorebooleanWhether more items are available.
loaderReact.ReactNodeOptional custom loader component.