← Components/Loaders

InfiniteScroll

infinitescroll-1779394026027.tsx
'use client'
import { useState, useRef, useCallback, useEffect } from 'react'

function makeItems(start, count) {
  return Array.from({ length: count }, (_, i) => ({
    id: start + i,
    title: 'Item ' + (start + i),
    desc: 'Description for item number ' + (start + i),
  }))
}

export default function InfiniteScroll() {
  const [items, setItems] = useState(makeItems(1, 8))
  const [loading, setLoading] = useState(false)
  const [hasMore, setHasMore] = useState(true)
  const loader = useRef(null)
  const load = useCallback(() => {
    if (loading || !hasMore) return
    setLoading(true)
    setTimeout(() => {
      const next = makeItems(items.length + 1, 4)
      setItems(prev => [...prev, ...next])
      if (items.length + 4 >= 24) setHasMore(false)
      setLoading(false)
    }, 800)
  }, [items.length, loading, hasMore])
  useEffect(() => {
    const obs = new IntersectionObserver(e => { if (e[0].isIntersecting) load() }, { threshold: 0.1 })
    if (loader.current) obs.observe(loader.current)
    return () => obs.disconnect()
  }, [load])
  return (
    <div style={{ maxHeight:'400px', overflowY:'auto', display:'flex', flexDirection:'column', gap:'8px', padding:'4px' }}>
      {items.map(item => (
        <div key={item.id} style={{
          background:'#fff', borderRadius:'12px', padding:'14px 16px',
          boxShadow:'0 1px 6px rgba(0,0,0,0.06)', display:'flex', gap:'12px', alignItems:'center',
        }}>
          <div style={{ width:'40px', height:'40px', borderRadius:'10px', background:'linear-gradient(135deg,#6d28d9,#2563eb)', display:'flex', alignItems:'center', justifyContent:'center', color:'#fff', fontWeight:700, fontSize:'14px', flexShrink:0 }}>#{item.id}</div>
          <div>
            <div style={{ fontWeight:600, color:'#1e293b', fontSize:'14px' }}>{item.title}</div>
            <div style={{ fontSize:'12px', color:'#94a3b8' }}>{item.desc}</div>
          </div>
        </div>
      ))}
      <div ref={loader} style={{ padding:'12px', textAlign:'center' }}>
        {loading && <div style={{ fontSize:'13px', color:'#94a3b8' }}>Loading more... ⏳</div>}
        {!hasMore && <div style={{ fontSize:'13px', color:'#94a3b8' }}>All items loaded ✓</div>}
      </div>
    </div>
  )
}

Component info

CategoryLoaders
Frameworkreact
TierFREE
Views0
Copies0

About

Infinite scroll list with load-more trigger and skeleton state

More from Loaders

'use client'
import { useState, useEffect } from 'react'

export default function LoaderCollection() {
  const [progress, setProgress] = useState(0)
  const [matrix, setMatrix] = useState<string[]>([])

  useEffect(() => {
    const t = setInterval((
LoaderCollection
Loaders
'use client';
import { useEffect, useRef } from 'react';

export default function WaveLoader({ bars = 8, color = "#C9A84C", label = "Processing..." }) {
  const refs = useRef([]);

  useEffect(() => {
    const animations = refs.current.map((el, i) =
WaveLoader
Loaders
'use client'
export default function SpinnerLoader({ type = 'ring', size = 48, color = '#6d28d9' }) {
  const s = size
  const style = {
    ring: {
      width: s, height: s, borderRadius: '50%',
      border: s/8 + 'px solid #e2e8f0',
      borderT
SpinnerLoader
Loaders
'use client'
import { useEffect, useState } from 'react'

export default function ProgressBar({
  value = 75, animated = true, label = 'Upload Progress',
  color = 'linear-gradient(90deg, #6d28d9, #2563eb)',
}) {
  const [width, setWidth] = useState(
ProgressBar
Loaders