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

Cum faci server-side filtering și pagination cu shadcn/ui în Next.js, fără să o iei razna

De Gabriela Neagu, 4 iul. 2026 · 17 vizualizări · 2 like-uri

Postat 4 iul. 2026
typescript
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useCallback, useTransition } from "react";

export function useTableNavigation() {
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const [isPending, startTransition] = useTransition();

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

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

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

  return { updateParams, isPending };
}

Am avut recent de făcut un dashboard pentru un client cu vreo 85.000 de tranzacții în baza de date. Evident, varianta clasică de client-side filtering din documentația shadcn/ui murea instant sau bloca browserul la prima căutare mai serioasă. Soluția a fost să mutăm totul pe server-side folosind URL search params din Next.js ca „single source of truth”.

În loc să ții starea tabelului în useState local, lași URL-ul să conducă totul. Când userul dă click pe pagina 2, pui ?page=2 în URL. Next.js re-randeză Server Component-ul, trage datele noi din baza de date și le trimite înapoi curate.

De ce URL-ul este cel mai bun state manager

Marele avantaj când legi tabelul de search params este că rezolvi nativ trei mari probleme de UX. Userul poate da share la linkul exact cu filtrele aplicate, butonul de „back” din browser funcționează ca prin minune, iar dacă dă refresh la pagină nu își pierde progresul. Am economisit cam 30% din codul de sincronizare pe care l-aș fi scris cu un useEffect chinuit.

Dar există și un trade-off destul de enervant. Fiecare tastă apăsată în inputul de filtrare declanșează un request pe server. Dacă baza ta de date nu are index pe coloana filtrată, o să ai un lag masiv.

Cum rezolvăm lagul la filtrare

Ca să nu omorâm baza de date la fiecare caracter introdus de utilizator, avem nevoie de un input debounced. Acesta ține o stare locală rapidă, dar face update la URL abia după 300-500ms de inactivitate.

Aici intervine Next.js useTransition. Când navighezi programatic prin router.push, Next.js blochează uneori UI-ul pentru câteva milisecunde până când Server Component-ul termină de luat datele. Înfășurând update-ul de URL în startTransition, îi spui React-ului să păstreze interfața interactivă în timp ce încarcă noile date în fundal. Poți chiar să afișezi un spinner discret peste tabel pentru a semnala că se întâmplă ceva.

Trade-off-uri pe care trebuie să le accepți

Abordarea asta funcționează genial pentru tabele mari, dar vine cu niște costuri de dezvoltare. În primul rând, codul devine considerabil mai complex decât cel din documentația standard shadcn. Trebuie să serializezi și să deserializezi manual toate filtrele în URL (de exemplu, cum trimiți un array de statusuri selectate? Probabil ca ?status=pending,success).

În al doilea rând, dacă ai nevoie de selecție multi-row care să persiste între pagini, treaba devine și mai complicată, deoarece selecția se face de obicei pe ID-uri de rânduri care nu sunt încă încărcate în memorie.

Voi cum gestionați tabelele mari în Next.js? Rămâneți pe client-side până la o anumită limită sau mutați totul pe server din prima zi?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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