shadcn/ui içinde tek komutla kurulan hazır bir “data table” bileşeni yoktur. Bunun yerine Table adında sade bir tablo bileşeni ve bu tabloyu TanStack Table (@tanstack/react-table) ile birleştirip kendi veri tablonuzu adım adım kurduğunuz bir rehber vardır. Bu yazıda o rehberin ilk yarısını izliyoruz: Table ilkelleri, kurulum, örnek veri, sütun tanımları, <DataTable /> bileşeni, sayfada gösterme, hücre biçimlendirme ve satır aksiyonları.

Sıralama, filtreleme, sayfalama, sütun gizleme ve satır seçimi gibi etkileşimli özellikler ikinci yazının konusudur: shadcn Data Table: sıralama, filtreleme ve sayfalama. Serinin tamamı için shadcn/ui öğrenme rehberi sayfasına bakabilirsiniz. Anlatılanlar depo kopyası 19 Aralık 2025 tarihli shadcn/ui belgelerine ve new-york-v4 stilindeki bileşen kaynak koduna dayanır.

Table bileşeni ne sunar?

Table bileşeni ne sunar? Table, TableHeader, TableBody, TableFooter

Belgede “responsive table component” olarak tanıtılan Table, aslında HTML tablo öğelerinin üzerine shadcn stillerini giydiren küçük fonksiyonlardan oluşur. Kaynak dosyası components/ui/table.tsx içinde her parça, karşılık geldiği HTML öğesini döndürür, gelen className değerini cn() ile varsayılan sınıflarla birleştirir ve kalan tüm prop’ları öğeye aktarır. Yani bir TableCell üzerine yazabileceğiniz her şey, normal bir td öğesine yazabileceğiniz şeydir: colSpan, className, olay işleyicileri ve benzerleri.

Kurulum için CLI komutu yeterlidir. Elle kurulum seçeneğinde ise belge, kaynak kodu kopyalayıp içe aktarma yollarını kendi projenize göre düzeltmenizi ister; ek bir npm bağımlılığı yoktur.

bash
npx shadcn@latest add table

Aşağıdaki tablo, kaynak koddaki parçaları ve her birinin hangi öğeyi ürettiğini özetler. data-slot değerleri, Şubat 2025 Tailwind v4 güncellemesiyle her ilkele eklenen ve stil verirken hedeflemeyi kolaylaştıran özniteliklerdir; ayrıntısı cn() ve data-slot yazısında anlatılıyor.

BileşenÜrettiği öğedata-slotÖne çıkan varsayılan stil
Tablediv içinde tabletable-container ve tableDış div yatay kaydırma (overflow-x-auto), tablo tam genişlik
TableHeadertheadtable-headerSatırlara alt kenarlık
TableBodytbodytable-bodySon satırın kenarlığı kaldırılır
TableFootertfoottable-footerSoluk arka plan, üst kenarlık, kalın yazı
TableRowtrtable-rowÜzerine gelince ve data-state=selected iken muted arka plan
TableHeadthtable-headSola hizalı, satır kırılmaz (whitespace-nowrap)
TableCelltdtable-cellİç boşluk, satır kırılmaz
TableCaptioncaptiontable-captionTablonun altında soluk küçük yazı

Bu tabloda iki ayrıntı ileride işinize yarayacak. Birincisi, Table bileşeni tabloyu doğrudan değil overflow-x-auto sınıflı bir div içinde döndürür; belgedeki “responsive” ifadesinin karşılığı budur: dar ekranda tablo taşarsa yatay kaydırma çıkar. İkincisi, TableRow stilinde data-[state=selected]:bg-muted sınıfı bulunur. Satıra data-state="selected" özniteliği verdiğinizde satır vurgulanır. Data table rehberindeki satır seçimi bu kancayı kullanır.

TableHead ve TableCell sınıflarında ayrıca içinde role=checkbox taşıyan bir öğe varsa sağ boşluğu sıfırlayan ve onay kutusunu birkaç piksel aşağı kaydıran seçiciler vardır. Bu, seçim sütunundaki Checkbox bileşeninin hücrede düzgün hizalanması için düşünülmüştür.

Düz bir tablo: Table ilkellerini kullanmak

Önce TanStack olmadan, yalnızca ilkellerle bir tablo kuralım. Belgedeki örnek bir fatura listesidir; burada aynı yapıyı Türkçe etiketlerle kullanıyoruz. İçe aktarma yolu, components.json içindeki ui takma adına göre @/components/ui/table olur.

typescript
import {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableFooter,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table"

const faturalar = [
  { no: "INV001", durum: "Ödendi", yontem: "Kredi kartı", tutar: "$250.00" },
  { no: "INV002", durum: "Bekliyor", yontem: "PayPal", tutar: "$150.00" },
  { no: "INV003", durum: "Ödenmedi", yontem: "Havale", tutar: "$350.00" },
]

export function FaturaTablosu() {
  return (
    <Table>
      <TableCaption>Son faturalarınızın listesi.</TableCaption>
      <TableHeader>
        <TableRow>
          <TableHead className="w-[100px]">Fatura</TableHead>
          <TableHead>Durum</TableHead>
          <TableHead>Yöntem</TableHead>
          <TableHead className="text-right">Tutar</TableHead>
        </TableRow>
      </TableHeader>
      <TableBody>
        {faturalar.map((f) => (
          <TableRow key={f.no}>
            <TableCell className="font-medium">{f.no}</TableCell>
            <TableCell>{f.durum}</TableCell>
            <TableCell>{f.yontem}</TableCell>
            <TableCell className="text-right">{f.tutar}</TableCell>
          </TableRow>
        ))}
      </TableBody>
      <TableFooter>
        <TableRow>
          <TableCell colSpan={3}>Toplam</TableCell>
          <TableCell className="text-right">$750.00</TableCell>
        </TableRow>
      </TableFooter>
    </Table>
  )
}

Bu kodda veri bir dizi, satırlar map ile üretiliyor ve tutar sütunu text-right ile sağa yaslanıyor. TableFooter resmî demoda da toplam satırı için kullanılır; colSpan={3} ilk üç sütunu tek hücrede birleştirir. Sıralama, arama ya da sayfalama yoksa bu kadarı çoğu zaman yeterlidir; TanStack Table’a ancak tablo etkileşimli hâle gelmeye başladığında ihtiyaç duyarsınız.

Neden hazır bir DataTable bileşeni yok?

Belgenin girişinde yazar, yaptığı her veri tablosunun farklı çıktığını anlatır: her birinin sıralama ve filtreleme ihtiyacı başka, veri kaynağı başkadır. Bütün bu varyasyonları tek bir bileşene doldurmak, TanStack Table’ın sağladığı “headless UI” esnekliğini kaybettirir. Bu yüzden shadcn/ui bir bileşen değil, kendi tablonuzu nasıl kuracağınızı gösteren bir kılavuz sunar.

“Headless” burada şu anlama gelir: TanStack Table satırları, sütunları, sıralama ve filtreleme durumunu hesaplar ama hiçbir HTML üretmez. Görünümü siz kurarsınız; shadcn’in Table ilkelleri de tam bu görünüm katmanını doldurur. Sonuçta tablonun mantığı TanStack’te, görünümü sizin projenizdeki table.tsx dosyasında kalır. Bu, shadcn/ui’nin genel “kodu projenize kopyalayın” yaklaşımıyla da uyumludur; yaklaşımın kendisi shadcn/ui nedir yazısında anlatılıyor.

Kurulum: Table ve TanStack Table

Rehber iki adımla başlar: Table bileşenini CLI ile eklemek ve TanStack Table paketini npm ile kurmak. Belge yalnızca npm komutunu gösterir; pnpm, yarn ya da bun kullanıyorsanız aynı paketi kendi paket yöneticinizle eklersiniz. Node.js ve npm kurulumu için Node.js kurulum rehberine bakabilirsiniz.

bash
npx shadcn@latest add table
npm install @tanstack/react-table

Rehberin ilerleyen adımlarında Button, DropdownMenu, Input ve Checkbox bileşenleri de kullanılır. Bunları ihtiyaç duydukça npx shadcn@latest add button gibi komutlarla ekleyebilirsiniz; CLI ayrıntıları shadcn CLI komutları yazısında.

Örnek veri ve proje yapısı

Örnek veri ve proje yapısı: app/payments/columns.tsx, app/payments/data-table.tsx, app/payments/page.tsx

Belge, son ödemeleri gösteren bir tablo kurar. Her ödeme bir kimlik, tutar, durum ve e-posta taşır. Durum alanı dört sabit değerden biridir: pending, processing, success, failed. Tipin TypeScript ile tanımlanması, sütun tanımlarında hangi alanların bulunduğunu derleyiciye bildirir; belgedeki yorum, isterseniz burada bir Zod şeması da kullanabileceğinizi belirtir.

typescript
type Payment = {
  id: string
  amount: number
  status: "pending" | "processing" | "success" | "failed"
  email: string
}

export const payments: Payment[] = [
  {
    id: "728ed52f",
    amount: 100,
    status: "pending",
    email: "m@example.com",
  },
  {
    id: "489e1d42",
    amount: 125,
    status: "processing",
    email: "example@gmail.com",
  },
  // ...
]

Dosya yapısı üç parçadan oluşur. Belge örneği Next.js üzerinde kurar ama aynı yapının başka React çatılarında da çalıştığını açıkça söyler.

DosyaBileşen türüGörevi
app/payments/columns.tsxİstemci bileşeniSütun tanımları
app/payments/data-table.tsxİstemci bileşeniDataTable bileşeni (tabloyu çizer)
app/payments/page.tsxSunucu bileşeniVeriyi getirir ve tabloyu gösterir

Bu ayrımın nedeni, TanStack Table’ın React hook’larıyla (useReactTable) çalışmasıdır; hook kullanan dosyalar Next.js App Router’da istemci bileşeni olmalıdır. Veriyi getiren sayfa ise sunucu bileşeni olarak kalabilir. Sunucu ve istemci bileşeni kavramları için Next.js App Router yazısına bakın.

Sütun tanımları: ColumnDef

Belgeye göre sütunlar, tablonuzun çekirdeğidir: hangi verinin gösterileceğini, nasıl biçimlendirileceğini, sıralanıp filtreleneceğini burada tanımlarsınız. En basit hâliyle her sütun bir accessorKey (veri nesnesindeki alan adı) ve bir header (başlık metni) taşır. app/payments/columns.tsx dosyasında:

typescript
"use client"

import { ColumnDef } from "@tanstack/react-table"

export type Payment = {
  id: string
  amount: number
  status: "pending" | "processing" | "success" | "failed"
  email: string
}

export const columns: ColumnDef<Payment>[] = [
  {
    accessorKey: "status",
    header: "Durum",
  },
  {
    accessorKey: "email",
    header: "E-posta",
  },
  {
    accessorKey: "amount",
    header: "Tutar",
  },
]

ColumnDef<Payment> tip parametresi sayesinde accessorKey değerleri Payment alanlarıyla eşleşir. Başlık metinleri Türkçe olabilir; accessorKey ise veri nesnesindeki gerçek anahtar adıyla aynı kalmalıdır. Sütunun kimliği de varsayılan olarak bu anahtardan gelir; ikinci yazıda table.getColumn("email") gibi çağrılar bu kimliği kullanır.

DataTable bileşeni

Sırada tabloyu çizen bileşen var. Bu bileşen sütunları ve veriyi prop olarak alır, useReactTable ile bir tablo örneği oluşturur ve shadcn ilkelleriyle başlıkları ve satırları çizer. app/payments/data-table.tsx dosyasında (yalnızca “No results.” metni Türkçeleştirildi):

typescript
"use client"

import {
  ColumnDef,
  flexRender,
  getCoreRowModel,
  useReactTable,
} from "@tanstack/react-table"

import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table"

interface DataTableProps<TData, TValue> {
  columns: ColumnDef<TData, TValue>[]
  data: TData[]
}

export function DataTable<TData, TValue>({
  columns,
  data,
}: DataTableProps<TData, TValue>) {
  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
  })

  return (
    <div className="overflow-hidden rounded-md border">
      <Table>
        <TableHeader>
          {table.getHeaderGroups().map((headerGroup) => (
            <TableRow key={headerGroup.id}>
              {headerGroup.headers.map((header) => {
                return (
                  <TableHead key={header.id}>
                    {header.isPlaceholder
                      ? null
                      : flexRender(
                          header.column.columnDef.header,
                          header.getContext()
                        )}
                  </TableHead>
                )
              })}
            </TableRow>
          ))}
        </TableHeader>
        <TableBody>
          {table.getRowModel().rows?.length ? (
            table.getRowModel().rows.map((row) => (
              <TableRow
                key={row.id}
                data-state={row.getIsSelected() && "selected"}
              >
                {row.getVisibleCells().map((cell) => (
                  <TableCell key={cell.id}>
                    {flexRender(cell.column.columnDef.cell, cell.getContext())}
                  </TableCell>
                ))}
              </TableRow>
            ))
          ) : (
            <TableRow>
              <TableCell colSpan={columns.length} className="h-24 text-center">
                Sonuç bulunamadı.
              </TableCell>
            </TableRow>
          )}
        </TableBody>
      </Table>
    </div>
  )
}

Bu bileşeni satır satır okuyalım, çünkü ikinci yazıdaki bütün özellikler bu iskeletin üzerine eklenir:

  • useReactTable çağrısı tablo örneğini üretir. getCoreRowModel: getCoreRowModel() zorunlu temel satır modelidir; sayfalama, sıralama ve filtreleme için ileride başka satır modelleri eklenir.
  • table.getHeaderGroups() başlık satırlarını, her grubun headers dizisi de başlık hücrelerini verir. header.isPlaceholder doğruysa hücre boş bırakılır; aksi hâlde başlık içeriği flexRender ile çizilir.
  • flexRender, sütun tanımındaki header ya da cell değeri düz metin de olsa bir fonksiyon da olsa onu doğru biçimde gösterir. Biçimlendirme ve aksiyon sütunlarında fonksiyon kullanacağımız için bu önemlidir.
  • table.getRowModel().rows o an gösterilecek satırlardır. Satır yoksa tüm sütunları kaplayan (colSpan={columns.length}) tek bir hücreyle boş durum mesajı gösterilir.
  • data-state={row.getIsSelected() && "selected"} satır seçili olduğunda TableRow stilindeki vurguyu tetikler. Seçim henüz eklenmediği için şimdilik etkisizdir.
  • Tablo, overflow-hidden rounded-md border sınıflı bir div ile sarılır; köşeleri yuvarlatılmış çerçeve buradan gelir.

TData ve TValue, bileşenin her veri tipiyle çalışabilmesi için kullanılan generic tip parametreleridir. Generic kavramı TypeScript konusudur; ayrıntısı için TypeScript öğrenme rehberine bakabilirsiniz. Burada bilmeniz gereken, bu sayede aynı DataTable bileşenini ödemeler için de kullanıcılar için de kullanabileceğinizdir.

Tabloyu sayfada göstermek

Son adımda sayfa bileşeni veriyi getirir ve tabloya verir. Belgede getData fonksiyonu sabit bir dizi döndürür; yorum satırı gerçek projede verinin buradan API’nizden çekileceğini söyler. app/payments/page.tsx dosyasında:

typescript
import { columns, Payment } from "./columns"
import { DataTable } from "./data-table"

async function getData(): Promise<Payment[]> {
  // Veriyi burada API'nizden getirin.
  return [
    {
      id: "728ed52f",
      amount: 100,
      status: "pending",
      email: "m@example.com",
    },
    // ...
  ]
}

export default async function DemoPage() {
  const data = await getData()

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

Sayfa bir async sunucu bileşenidir; veri sunucuda alınır ve hazır hâlde istemci bileşeni olan DataTable’a aktarılır. columns dizisi de istemci dosyasından içe aktarılır. Vite gibi sunucu bileşeni olmayan bir ortamda aynı fikri, veriyi bir hook ya da veri getirme kütüphanesiyle alıp DataTable’a geçirerek uygularsınız; belge bu varyantı ayrıca göstermez.

Hücre biçimlendirme

Ham sayıyı para birimi olarak göstermek ve sütunu sağa yaslamak için header ve cell alanlarına fonksiyon verilir. cell fonksiyonu row nesnesini alır; row.getValue("amount") o satırın tutar değerini döndürür. Belge biçimlendirme için Intl.NumberFormat kullanır (orijinalde en-US ve USD; aşağıda yalnızca yerel ayarı ve para birimini değiştirdik). app/payments/columns.tsx dosyasında:

typescript
export const columns: ColumnDef<Payment>[] = [
  {
    accessorKey: "amount",
    header: () => <div className="text-right">Tutar</div>,
    cell: ({ row }) => {
      const amount = parseFloat(row.getValue("amount"))
      const formatted = new Intl.NumberFormat("tr-TR", {
        style: "currency",
        currency: "TRY",
      }).format(amount)

      return <div className="text-right font-medium">{formatted}</div>
    },
  },
]

Burada iki şeye dikkat edin. Başlık da text-right ile sağa yaslanmıştır; yalnızca hücreyi yaslarsanız başlık ile değerler hizasız görünür. İkincisi, biçimlendirme yalnızca görünümü değiştirir; alttaki veri yine sayı olarak kalır. Bu sayede ikinci yazıda ekleyeceğimiz sıralama, biçimlenmiş metne göre değil gerçek sayıya göre çalışır. Belge aynı yaklaşımın diğer hücrelerde ve başlıklarda da kullanılabileceğini söyler; örneğin status değerini bir Badge bileşeni ile renkli rozet olarak gösterebilirsiniz.

Satır aksiyonları

Her satırın sonuna “kimliği kopyala”, “müşteriyi görüntüle” gibi işlemler içeren bir menü eklemek yaygın bir ihtiyaçtır. Belge bunun için id: "actions" kimlikli, accessorKey taşımayan bir sütun ekler ve hücresinde bir DropdownMenu döndürür. app/payments/columns.tsx dosyasında:

typescript
"use client"

import { ColumnDef } from "@tanstack/react-table"
import { MoreHorizontal } from "lucide-react"

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export const columns: ColumnDef<Payment>[] = [
  // ...
  {
    id: "actions",
    cell: ({ row }) => {
      const payment = row.original

      return (
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button variant="ghost" className="h-8 w-8 p-0">
              <span className="sr-only">Menüyü aç</span>
              <MoreHorizontal className="h-4 w-4" />
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end">
            <DropdownMenuLabel>İşlemler</DropdownMenuLabel>
            <DropdownMenuItem
              onClick={() => navigator.clipboard.writeText(payment.id)}
            >
              Ödeme kimliğini kopyala
            </DropdownMenuItem>
            <DropdownMenuSeparator />
            <DropdownMenuItem>Müşteriyi görüntüle</DropdownMenuItem>
            <DropdownMenuItem>Ödeme ayrıntıları</DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      )
    },
  },
  // ...
]

Kilit nokta row.original’dır: hücre fonksiyonu içinde satırın ham veri nesnesine bu özellikle erişirsiniz. Belge, örneğin buradaki id değerini kullanarak API’nize bir DELETE isteği gönderebileceğinizi söyler. Menü tetikleyicisi yalnızca ikon içeren bir ghost butondur; ekranda görünmeyen sr-only metni, ekran okuyucuya butonun ne işe yaradığını anlatır. asChild kullanımı ve buton varyantları için Button yazısına, erişilebilir etiket kavramı için ARIA yazısına bakabilirsiniz.

Aksiyon sütununun accessorKey taşımaması bilinçlidir: bu sütun bir veri alanını göstermez, yalnızca arayüz öğesi barındırır. Bu nedenle kimliği id ile elle verilir. İkinci yazıdaki sütun görünürlüğü bileşeni de tam bu özelliğe bakarak (erişimci fonksiyonu olmayan sütunları) listeden çıkarır.

Küçük bir örnek: kullanıcı tablosu

Aynı iskeleti başka bir veriye uygulamak, yapının ne kadar genel olduğunu gösterir. Diyelim ki kullanıcıları ad, e-posta ve rol ile listelemek istiyorsunuz. DataTable bileşenine dokunmanız gerekmez; yalnızca yeni bir tip ve sütun dizisi yazarsınız:

typescript
"use client"

import { ColumnDef } from "@tanstack/react-table"

export type Kullanici = {
  id: string
  ad: string
  email: string
  rol: "yonetici" | "editor" | "okuyucu"
}

export const kullaniciSutunlari: ColumnDef<Kullanici>[] = [
  { accessorKey: "ad", header: "Ad" },
  { accessorKey: "email", header: "E-posta" },
  {
    accessorKey: "rol",
    header: "Rol",
    cell: ({ row }) => (
      <span className="capitalize">{row.getValue("rol")}</span>
    ),
  },
]

Sayfada <DataTable columns={kullaniciSutunlari} data={kullanicilar} /> yazmanız yeterlidir. Generic tip parametreleri, sütunlar ile verinin aynı tipte olduğunu derleme sırasında denetler. Belgenin “tabloyu yeniden kullanılabilir bileşene çıkarın” önerisinin pratikteki karşılığı budur.

Sık yapılan hatalar

Sık yapılan hatalar: Hazır bileşen aramak., “use client” satırını unutmak., accessorKey ile veri anahtarının uyuşmaması., Biçimlenmiş metni veriye yazmak.
  • Hazır bileşen aramak. Belgede veri tablosu için ayrı bir kurulum komutu yoktur; kurulum npx shadcn@latest add table ve @tanstack/react-table paketiyle başlar, gerisini siz yazarsınız.
  • “use client” satırını unutmak. columns.tsx ve data-table.tsx belgede istemci bileşeni olarak işaretlidir. Hook kullanan dosyayı sunucu bileşeni olarak bırakmak Next.js’te hataya yol açar.
  • accessorKey ile veri anahtarının uyuşmaması. accessorKey: "mail" yazıp veride email kullanırsanız sütun boş görünür. Türkçe olması gereken başlıktır, anahtar değil.
  • Biçimlenmiş metni veriye yazmak. Tutarları API’den “₺1.250,00” gibi metin olarak getirirseniz sıralama ya da toplam hesabı için onu yeniden sayıya çevirmeniz gerekir. Belgedeki gibi sayıyı veride tutun, biçimi cell içinde verin.
  • Boş durumu atlamak. Belgedeki kod, satır yoksa colSpan={columns.length} ile tek bir mesaj hücresi gösterir. Bunu silerseniz filtre sonucu boş kaldığında tablo yalnızca başlıktan ibaret görünür.
  • Aksiyon menüsünde metinsiz ikon butonu. sr-only açıklamasını kaldırmak, butonu ekran okuyucu kullanıcıları için anlamsız bırakır.

Sık Sorulan Sorular

shadcn/ui Data Table için ayrı bir paket kurmam gerekir mi?

Evet, tek ek paket @tanstack/react-table’dır. Görünüm katmanı olan Table bileşeni CLI ile projenize kopyalanır; tablonun mantığı ise TanStack Table’dan gelir.

Bu rehber yalnızca Next.js ile mi çalışır?

Hayır. Belge örneği Next.js ile anlatır ama diğer React çatılarında da çalıştığını belirtir. Sunucu bileşeni olmayan ortamlarda veriyi istemci tarafında getirip aynı DataTable bileşenine verirsiniz.

Sadece birkaç satırlık statik bir tablo için TanStack gerekir mi?

Gerekmez. Sıralama, filtre ve sayfalama yoksa Table, TableHeader, TableRow ve TableCell ile düz bir tablo kurmak yeterlidir; yukarıdaki fatura örneği bunu gösterir.

Satır verisine bir butondan nasıl erişirim?

cell fonksiyonuna gelen row nesnesinin row.original özelliği satırın ham verisidir. Silme, düzenleme gibi işlemlerde kimliği buradan alırsınız.

Sıralama ve sayfalamayı nasıl eklerim?

Bu özellikler useReactTable çağrısına ek satır modelleri ve durum değişkenleri eklenerek kurulur. Adım adım anlatımı ikinci Data Table yazısında bulabilirsiniz.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: shadcn/ui öğrenme rehberi · Data Table: sıralama, filtreleme, sayfalama · Dropdown Menu · Button varyantları · cn() fonksiyonu · Next.js App Router