Communitygithub.com

Desplazamiento Infinito

Paginación basada en cursor, IntersectionObserver, listas virtualizadas con @tanstack/react-virtual y restauración de la posición de desplazamiento.

¿Qué es Desplazamiento Infinito?

Esta habilidad implementa el desplazamiento infinito (infinite scroll) en aplicaciones web, utilizando técnicas de paginación basada en cursor, observación de intersección (IntersectionObserver), listas virtualizadas con @tanstack/react-virtual, y restauración de la posición de desplazamiento. Es especialmente útil para listas largas (1,000+ elementos) donde se necesita un rendimiento óptimo, evitando renderizar todos los nodos DOM. La habilidad proporciona reglas claras para manejar estados de carga, indicadores de fin de lista, estados vacíos y un botón 'Cargar más' como alternativa para usuarios con conexiones lentas. Incluye patrones de código con React Query e IntersectionObserver para la carga de datos paginados, y ejemplos de virtualización con @tanstack/react-virtual para listas de más de 10,000 elementos. Se recomienda evitar la paginación basada en offset, la carga completa de todos los elementos en el cliente, y la falta de indicador de fin de lista.

Compatible con~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/YepAPI/skills/tree/HEAD/skills/infinite-scroll

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

¿Qué hace Desplazamiento Infinito?

Reglas

  • Paginación basada en cursor: usa el ID o marca de tiempo del último elemento como cursor — no usar offset, que se rompe con inserciones/eliminaciones
  • IntersectionObserver: conéctalo a un elemento centinela en la parte inferior de la lista — activa la obtención de datos cuando sea visible
  • Virtualización: @tanstack/react-virtual para listas con 1,000+ elementos — renderiza solo las filas visibles en el DOM
  • Esqueleto de carga: muestra elementos de marcador de posición en la parte inferior mientras se obtiene la siguiente página — no un spinner
  • Botón "Cargar más": proporciona como alternativa para usuarios que prefieren control manual o tienen conexiones lentas
  • Restauración de la posición de desplazamiento: guarda la posición de desplazamiento antes de la navegación, restaura al regresar — usa sessionStorage o el estado del enrutador
  • Estado vacío: muestra un mensaje cuando la lista tiene cero elementos — no una página en blanco
  • Fin de la lista: muestra "Has llegado al final" cuando hasNextPage sea falso — detén el observador

Patrones

// Desplazamiento infinito con React Query + IntersectionObserver
import { useInfiniteQuery } from "@tanstack/react-query";
import { useRef, useEffect } from "react";

function InfiniteList() {
  const sentinelRef = useRef<HTMLDivElement>(null);
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
    queryKey: ["items"],
    queryFn: ({ pageParam }) => fetchItems({ cursor: pageParam, limit: 20 }),
    getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
    initialPageParam: undefined as string | undefined,
  });

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => { if (entries[0].isIntersecting && hasNextPage) fetchNextPage(); },
      { rootMargin: "200px" } // precargar antes de que el usuario llegue al fondo
    );
    if (sentinelRef.current) observer.observe(sentinelRef.current);
    return () => observer.disconnect();
  }, [hasNextPage, fetchNextPage]);

  const allItems = data?.pages.flatMap((page) => page.items) ?? [];

  return (
    <div>
      {allItems.map((item) => <ItemCard key={item.id} item={item} />)}
      {isFetchingNextPage && <LoadingSkeleton count={3} />}
      {!hasNextPage && allItems.length > 0 && <p>Has llegado al final</p>}
      {allItems.length === 0 && !isFetchingNextPage && <EmptyState />}
      <div ref={sentinelRef} />
    </div>
  );
}

// Lista virtualizada para más de 10k elementos
import { useVirtualizer } from "@tanstack/react-virtual";

function VirtualList({ items }: { items: Item[] }) {
  const parentRef = useRef<HTMLDivElement>(null);
  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 64, // altura estimada de la fila en px
    overscan: 5,
  });

  return (
    <div ref={parentRef} style={{ height: "600px", overflow: "auto" }}>
      <div style={{ height: `${virtualizer.getTotalSize()}px`, position: "relative" }}>
        {virtualizer.getVirtualItems().map((virtualRow) => (
          <div key={virtualRow.key} style={{
            position: "absolute", top: 0, left: 0, width: "100%",
            transform: `translateY(${virtualRow.start}px)`,
          }}>
            <ItemCard item={items[virtualRow.index]} />
          </div>
        ))}
      </div>
    </div>
  );
}

Evitar

  • Paginación basada en offset — las inserciones/eliminaciones desplazan todos los offsets, causando elementos duplicados o faltantes
  • Cargar todos los elementos y luego filtrar en el cliente — obten solo la página actual del servidor
  • Renderizar más de 10,000 nodos DOM — usa @tanstack/react-virtual para renderizar solo las filas visibles
  • IntersectionObserver sin rootMargin — agrega un margen de 100-200px para precargar antes de que el usuario llegue al fondo
  • Falta de indicador de fin de lista — los usuarios necesitan saber que no hay más contenido para cargar

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

YepAPI/skills

WCAG 2.1 AA — semantic HTML, keyboard navigation, screen readers.

YepAPI/skills

Experiment setup, variant splitting, statistical significance, and feature flag integration.

YepAPI/skills

CRUD generators, data tables, user management, role-based access, and bulk operations.

YepAPI/skills

Monitor what ChatGPT and Gemini say about your brand using YepAPI.

YepAPI/skills

Queue workers with BullMQ/Inngest/Trigger.dev, job retries, dead letter queues, concurrency.

YepAPI/skills

Commander.js, interactive prompts with Clack, spinners, colors, config files, exit codes, and npm publishing.

YepAPI/skills

cmdk integration, Cmd+K trigger, fuzzy search, grouped actions, keyboard navigation, and dynamic action registration.

YepAPI/skills

Content planning with trend and sentiment data from YepAPI.

YepAPI/skills

Content calendar, keyword-driven planning, and content briefs.

YepAPI/skills

Theme switching with CSS variables, system preference detection, next-themes, and persistence.

YepAPI/skills

Threaded comments, @mentions, reactions, moderation queue, optimistic UI, and cursor pagination.

YepAPI/skills

Competitive analysis between domains using YepAPI.

YepAPI/skills

Layout grids, KPI cards, data tables with sorting/filtering, sidebar navigation, and responsive breakpoints.

YepAPI/skills

Data encryption — TLS everywhere, AES-256-GCM for PII, bcrypt passwords, encrypted backups.

YepAPI/skills

Chart patterns with Recharts/Nivo/D3, responsive containers, color scales, and accessibility.

YepAPI/skills

Drizzle schema definition, push vs migrate, type-safe queries, relations, transactions, and prepared statements.

YepAPI/skills

Upload handling, resize/compress, CDN delivery, blur placeholders, and next/image patterns.

YepAPI/skills

Local business and Google Maps research using YepAPI.

YepAPI/skills

Tenant isolation (row-level vs schema), subdomain routing, per-tenant config, and data partitioning.

YepAPI/skills

Chat UI components, streaming responses with AI SDK, context window management, and RAG patterns.

Skills relacionados