typescript 70 lines · 2 tabs

Infinite scroll with Intersection Observer

Maya Patel Jan 2026
2 tabs
import { useInfiniteQuery } from '@tanstack/react-query'
import api from '@/services/api'
import { Post, PaginatedResponse } from '@/types'

export function useInfinitePosts() {
  return useInfiniteQuery({
    queryKey: ['posts', 'infinite'],
    queryFn: async ({ pageParam = 1 }) => {
      const { data } = await api.get<PaginatedResponse<Post>>('/posts', {
        params: { page: pageParam },
      })
      return data
    },
    getNextPageParam: (lastPage) => {
      const { current_page, total_pages } = lastPage.meta
      return current_page < total_pages ? current_page + 1 : undefined
    },
    initialPageParam: 1,
  })
}
2 files · typescript Explain with highlit

Infinite scroll loads more content as users reach the bottom of a list, improving perceived performance over pagination. I use the Intersection Observer API to detect when a sentinel element becomes visible, then trigger the next page fetch. React Query's useInfiniteQuery manages pagination state and combines pages into a flattened list. The getNextPageParam function extracts pagination metadata from the API response to determine if more pages exist. Each new page appends to the existing data array. I also provide a 'Load More' button as a fallback for users who prefer manual control or when JavaScript fails. This pattern works great for feeds, search results, or any long lists.


Related snips

typescript
export interface RetryOptions {
  retries: number;
  baseMs: number;
  maxMs: number;
  signal?: AbortSignal;
  onRetry?: (attempt: number, delay: number, err: unknown) => void;

Exponential backoff with jitter for retries

typescript reliability retry
by codesnips 2 tabs
ruby
class PostsController < ApplicationController
  def index
    @posts = Post.includes(:author)
                 .order(created_at: :desc)
                 .page(params[:page])
                 .per(10)

Turbo Frames: infinite scroll with lazy-loading frame

rails turbo hotwire
by codesnips 4 tabs
typescript
import axios, { AxiosError } from 'axios'
import { v4 as uuidv4 } from 'uuid'

const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1',
  timeout: 15000,

Axios API client with interceptors

react axios api
by Maya Patel 1 tab
typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

interface FilterState {
  search: string
  category: string | null

Zustand for lightweight state management

react zustand state-management
by Maya Patel 2 tabs
typescript
import React from "react";

type FallbackProps = {
  error: Error;
  reset: () => void;
};

React Error Boundary + error reporting hook

react frontend error-boundary
by codesnips 3 tabs
javascript
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
  openAnalyzer: true,
});

/** @type {import('next').NextConfig} */

Next.js bundle analyzer for targeted performance work

nextjs performance tooling
by codesnips 4 tabs

Share this code

Here's the card — post it anywhere.

Infinite scroll with Intersection Observer — share card
Link copied