eduardweb.
shadcn/uiAvansat#performance#nextjs#react#shadcn-ui#tanstack-table

Cum am trecut de la client-side la server-side pagination în shadcn/ui și TanStack Table

De Paul Ene, 5 iun. 2026 · 2 vizualizări · 2 like-uri

Postat acum 4 zile
typescript
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useCallback } from 'react';

export function useTableNavigation() {
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();

  const updateQueryParams = useCallback((
    params: Record<string, string | number | null>
  ) => {
    const newParams = new URLSearchParams(searchParams.toString());
    
    Object.entries(params).forEach(([key, value]) => {
      if (value === null || value === '') {
        newParams.delete(key);
      } else {
        newParams.set(key, String(value));
      }
    });

    router.push(`${pathname}?${newParams.toString()}`, { scroll: false });
  }, [pathname, router, searchParams]);

  return { updateQueryParams };
}

Am văzut prea multe proiecte care folosesc shadcn/ui DataTable direct cu configurarea default pe client-side, deși au tabele cu mii de înregistrări. La 100 de rânduri e ok, dar când am trecut de 12.000 de înregistrări pe un dashboard de comenzi, browserul a început să gâfâie serios. Mutarea logicii pe server-side în Next.js (App Router) a fost singura soluție logică și am scăzut timpul de încărcare inițial de la 3.2 secunde la doar 180ms.

În acest ghid rapid îți explic cum să legi TanStack Table de URL-ul paginii, astfel încât utilizatorul să poată da share la un link cu filtrele deja aplicate.

De ce e greșit să lași totul pe client-side

Când instalezi componenta DataTable din shadcn/ui, primești un template care încarcă toate datele în memorie și face filtrarea și paginarea local. E simplu de scris, dar are două mari probleme: consumă memoria browserului și strică UX-ul dacă userul dă refresh (pierde complet pagina și filtrele pe care se afla).

Soluția corectă este să tratăm URL-ul ca pe o „sursă unică de adevăr”. Filtrele, numărul paginii și coloana de sortare trebuie să trăiască în searchParams-uri (?page=2&sort=createdAt_desc&search=ion).

Sincronizarea stării cu URL-ul (Search Params)

În loc să folosești stări locale de React (useState) pentru paginare sau sortare, vei folosi routerul din Next.js.

Când userul dă click pe „Next Page”, în loc să modifici un state intern, modifici URL-ul. Server Component-ul va prinde noile valori din searchParams, va interoga baza de date (folosind Prisma, Drizzle sau ce vrei tu) și va trimite doar cele 10 rânduri necesare înapoi către Client Component.

Compromisul: UX vs. număr de interogări în DB

Nimic nu e gratis pe lumea asta, iar abordarea asta vine cu un trade-off destul de mare.

  • Avantaj: Încărcare instantă a paginii, memorie minimă folosită în browser, link-uri share-uibile.
  • Dezavantaj: Fiecare apăsare de tastă în input-ul de căutare face un request la server. Dacă ai 500 de useri care scriu simultan, baza ta de date va fi bombardată.

Pentru a rezolva problema asta, trebuie obligatoriu să folosești un debounce de minimum 300ms pe input-ul de căutare înainte de a actualiza URL-ul. Altfel, o să ai probleme mari de performanță pe server.

În exemplul de mai jos, îți arăt cum scrii o funcție simplă de update pentru URL în componenta ta de tabel, folosind hook-urile din next/navigation.

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.