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

Server-side pagination, sortare și filtrare cu Shadcn DataTable în Next.js

De Elisabeta Stan, 9 iun. 2026 · 2 vizualizări · 2 like-uri

Postat acum 14 ore
typescript
// app/users/page.tsx
import { columns } from "./columns"
import { DataTable } from "./data-table"
import { db } from "@/lib/db"

interface Props {
  searchParams: {
    page?: string
    sort?: string
    order?: "asc" | "desc"
    q?: string
  }
}

export default async function UsersPage({ searchParams }: Props) {
  const page = Number(searchParams.page) || 1
  const limit = 10
  const sortBy = searchParams.sort || "createdAt"
  const order = searchParams.order || "desc"
  const query = searchParams.q || ""

  const skip = (page - 1) * limit

  // Facem fetch pe server direct din DB sau un API intern
  const [users, total] = await Promise.all([
    db.user.findMany({
      where: { name: { contains: query, mode: "insensitive" } },
      orderBy: { [sortBy]: order },
      take: limit,
      skip,
    }),
    db.user.count({ where: { name: { contains: query, mode: "insensitive" } } })
  ])

  const pageCount = Math.ceil(total / limit)

  return (
    <div className="container mx-auto py-10">
      <DataTable 
        columns={columns} 
        data={users} 
        pageCount={pageCount} 
      />
    </div>
  )
}

Să fim serioși, ghidul oficial de la Shadcn/ui pentru DataTable este excelent dacă ai de afișat 50 de rânduri în baza de date. Dar când am lucrat la un proiect de analytics cu peste 150.000 de înregistrări, încărcarea client-side pur și simplu a îngenuncheat browserul. Soluția corectă este mutarea întregii logici (paginare, filtrare, sortare) direct pe server, folosind Next.js App Router.

Am văzut mulți developeri care se chinuie să sincronizeze starea locală a tabelei cu baza de date. Haideți să vedem cum facem asta curat, fără să ne complicăm viața.

Capcana stării locale vs URL

Cea mai mare greșeală pe care am făcut-o la început a fost să folosesc starea internă din TanStack Table (sorting, pagination) și să fac fetch-uri din useEffect.

Într-o aplicație Next.js modernă, singura sursă de adevăr (source of truth) pentru tabelele server-side trebuie să fie URL-ul (prin query params). De ce?

  • Userul poate da share la link-ul /users?page=3&sort=email&order=desc iar altcineva va vedea exact aceeași pagină.
  • Butonul de back din browser funcționează nativ.
  • Nu ai probleme de desincronizare la re-randare.

Am pățit-o la un dashboard intern: userii trimiteau link-uri către clienți, dar toată lumea ajungea pe prima pagină, nesortată. A fost destul de jenant.

Cum legăm TanStack Table de Next.js searchParams

În loc să lași tabela să-și gestioneze singură starea prin useState, o transformi într-o componentă controlată de URL. Când userul dă click pe pagina următoare, nu apelezi o funcție locală, ci rulezi un update pe URL, de exemplu router.push('?page=2').

Next.js va intercepta schimbarea, va re-executa Server Component-ul și va trimite noile date direct către client. Tabela ta devine o simplă componentă de prezentare (dummy component).

Trade-off-ul de care nu-ți spune nimeni

Deși sună ideal, această abordare vine cu un cost destul de mare de dezvoltare. Trebuie să fii conștient de câteva aspecte înainte să te arunci la refactoring:

  • Latența: Pierzi tranzițiile ultra-rapide. Chiar și cu un server rapid și optimizări la DB, tot ai un delay de rețea de 100-200ms până când se randează noile date pe server. Pe client-side, filtrarea este instantanee.
  • Boilerplate: Trebuie să sanitizezi manual parametrii din query string. Dacă cineva introduce manual ?page=text-aiurea în URL, trebuie să te asiguri că backend-ul tău nu crapă când încearcă să convertească asta în număr.
  • Experiența de search: Pentru inputul de căutare, trebuie să folosești neapărat un debounce (de exemplu 300ms) înainte de a schimba URL-ul, altfel trimiți un request la server la fiecare caracter tastat.

Regula mea de aur: dacă ai sub 1.000 de rânduri în total, nu te complica cu server-side. Rămâi pe filtrare în client, e mult mai rapid de implementat și oferă o experiență mai fluidă.

Tu cum abordezi tabelele mari în Next.js? Rămâi pe searchParams sau preferi un state manager global?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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