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

Cum am trecut un shadcn/ui DataTable pe server-side în Next.js (fără dureri de cap)

De Bogdan Răducanu, 28 mai 2026 · 6 vizualizări · 3 like-uri

Postat 28 mai 2026
typescript
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useCallback } from 'react';

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

  const createQueryString = 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));
        }
      });

      return newParams.toString();
    },
    [searchParams]
  );

  const updateParams = (params: Record<string, string | number | null>) => {
    router.push(`${pathname}?${createQueryString(params)}`, { scroll: false });
  };

  return { searchParams, updateParams };
}

Am implementat recent shadcn/ui DataTable pe un proiect cu vreo 40.000 de înregistrări și am realizat rapid că varianta lor standard, de tip copy-paste, crapă lamentabil dacă ții toate datele în browser. Soluția e simplă în teorie, dar migăloasă în practică: trebuie să muți sortarea, filtrarea și paginarea direct în baza de date, folosind URL-ul ca singură sursă de adevăr.

În acest ghid rapid îți arăt cum am structurat eu totul ca să am performanță maximă, URL-uri share-uibile și un UX curat.

De ce crapă abordarea default pe client?

Când ai 100 de rânduri, e perfect să lași TanStack Table să facă totul în browser. Dar la peste 10.000 de înregistrări, nu doar că descarci aiurea mega-octeți de date pe care userul s-ar putea să nu le vadă niciodată, dar browserul începe să agațe la fiecare apăsare de tastă în câmpul de căutare.

Dacă muți totul pe server, trimiți către client doar cele 10 sau 20 de rânduri necesare pentru pagina curentă. În plus, dacă trimiți cuiva link-ul dashboard/users?page=3&sort=createdAt.desc, acea persoană va vedea exact aceleași date, lucru imposibil dacă ții starea tabelului într-un useState local.

Sincronizarea stării cu searchParams

Greșeala pe care am făcut-o la început a fost să folosesc useState pentru paginare și să dau fetch în useEffect de fiecare dată când se schimba pagina. Este o abordare greșită în Next.js App Router.

Modul corect este să folosești URL-ul ca stare. Când userul dă click pe pagina următoare sau schimbă sortarea, schimbi parametrii din URL folosind router.push. Next.js va re-randa automat Server Component-ul care face query-ul în baza de date.

Iată cum arată logica de actualizare a URL-ului direct din evenimentele oferite de TanStack Table:

Trade-off-uri sincere pe care trebuie să le știi

Nimic nu e perfect în web dev, iar abordarea asta vine cu un preț:

  • Avantaje: Memorie curată pe client, încărcare instantă a paginii inițiale și link-uri complet share-uibile.
  • Dezavantaje: Există o mică latență (network roundtrip) la fiecare click de paginare sau sortare. Spre deosebire de client-side unde totul e instantaneu, aici trebuie să aștepți răspunsul de la server.

Ca să ascunzi această latență și să nu pară aplicația blocată, îți recomand să folosești useTransition din React atunci când schimbi parametrii din URL. Astfel, poți afișa un spinner discret peste tabel în timp ce Next.js face fetch la noile date în fundal.

Tu cum procedezi cu tabelele mari în Next.js? Rămâi pe server-side pur sau preferi să aduci un client-side cache mai agresiv precum React Query?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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