פיתוח טבלאות אינטראקטיביות עם TanStack Table

טבלאות אינטראקטיביות הן יותר מסתם שורות ועמודות—הן כלי שעוזר למשתמשים למצוא במהירות את מה שהם צריכים ולקבל החלטות. אנחנו בונים פתרונות עם TanStack Table שהופכים את העבודה עם נתונים לנוחה וברורה. הצוות שלנו מספק את הפרויקט במפתח מלא, מתכנון המבנה ועד ליישום ותמיכה שוטפת, תוך הבטחת ביצועים יציבים וצמיחה יחד עם העסק שלכם.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
פיתוח טבלאות אינטראקטיביות עם TanStack Table
בינוני
~3-5 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1502
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1344
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1052
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1307
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1050
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1033

יישום טבלה אינטראקטיבית (TanStack Table) באתר

תארו לעצמכם: ממשק ניהול ההזמנות שלכם מתחיל לגמגם כבר ב-1000 שורות, ולקוחות מתלוננים על טעינה איטית של הדף. נתקלנו בזה עשרות פעמים — ואנחנו יודעים איך לתקן את זה. טבלה עם מיון ופיצ'ינג (pagination) היא אחת המשימות הנפוצות ביותר בפיתוח פרונטאנד, ואחת מהמשימות שמיושמות לרוב בצורה גרועה. jQuery DataTables נמצא במערכות legacy, אבל באפליקציות React מודרניות הסטנדרט דה פקטו הוא TanStack Table (לשעבר react-table). זוהי ספרייה ללא עיצוב מובנה (headless) ללא סגנונות מובנים, המעניקה שליטה מלאה על מבנה ה-HTML. גודל ~14 KB דחוס, אפילו קטן יותר עם Tree-shaking. מתוחזקת באופן פעיל, כוללת טיפוסי TypeScript, ותואמת ל-React Server Components ב-Next.js. אם אתם משתמשים ב-Vue, Solid או Svelte — קיימות גרסאות מותאמות.

בעיות שאנחנו פותרים

רוב הטבלאות המותאמות אישית סובלות מכמה טעויות אופייניות:

  • חוסר בווירטואליזציה — עם 5000 שורות ה-DOM עמוס, ו-React קופא למשך 2–3 שניות.
  • פיצ'ינג בצד הלקוח על מערכי נתונים גדולים — טעינת 50,000 שורות הורגת את רוחב הפס של הרשת ואת זיכרון הדפדפן.
  • אין debounce על סינון — כל הקלדה שולחת בקשה, ומעמיסה על השרת.

TanStack Table מטפל בזה ברמת הספרייה: אתם בוחרים רק את התכונות שאתם צריכים ומגדירים את ההתנהגות. היא לא כופה מודל נתונים — אתם מחליטים איך לאחסן ולטעון שורות. החלפת jQuery DataTables ב-TanStack Table יכולה להפחית את זמן הפיתוח ב-30%, ולהוריד את תקציב הפרויקט.

השוואת ספריות לטבלאות

ספרייה גודל גמישות ביצועים קהילה
TanStack Table v8 ~14 KB דחוס מלאה (headless) גבוהים (ווירטואליזציה) >30K כוכבי GitHub
jQuery DataTables ~80 KB (עם jQuery) נמוכה (כולל סגנונות) בינוניים (ללא ווירטואליזציה) Legacy
AG Grid Community ~200 KB דחוס גבוהה (תכונות רבות) גבוהים מאוד >10K כוכבי GitHub

זוהי הבחירה האופטימלית עבור React/Vue/Solid/Svelte. AG Grid מוצדק רק כשצריך עריכה בסגנון Excel על 100,000+ שורות. jQuery DataTables — אך ורק לתמיכה במערכות legacy. הספרייה מהירה פי 10 מ-jQuery DataTables בטיפול ב-10,000 שורות בזכות ווירטואליזציה והיעדר פעולות DOM מיותרות.

למה TanStack Table?

היא headless: ללא סגנונות מובנים, רק לוגיקה. אתם מחליטים איך הטבלה נראית — היא משתלבת בצורה מושלמת במערכת העיצוב שלכם. גודל ~14 KB דחוס, אפילו קטן יותר עם Tree-shaking. גרסה 8 תומכת ב-TypeScript וב-Server Components — עובדת בצורה חלקה ב-Next.js. כל מה שצריך זה להעביר נתונים ועמודות, וה-hook useReactTable מנהל את מצב המיון, הסינון והפיצ'ינג.

יישום בסיסי ב-React

התקנה:

npm install @tanstack/react-table 
דוגמה לרכיב טבלה עם חיפוש, מיון ופיצ'ינג
import { useState } from 'react'
import { ColumnDef, flexRender, getCoreRowModel, getSortedRowModel, getPaginationRowModel, getFilteredRowModel, useReactTable, SortingState, } from '@tanstack/react-table'

type Order = {
  id: string
  customer: string
  amount: number
  status: 'pending' | 'paid' | 'cancelled'
  createdAt: string
}

function OrdersTable({ data }: { data: Order[] }) {
  const [sorting, setSorting] = useState<SortingState>([])
  const [globalFilter, setGlobalFilter] = useState('')

  const columns: ColumnDef<Order>[] = [
    {
      accessorKey: 'id',
      header: '№ заказа',
      cell: info => <span className='font-mono text-sm'>{info.getValue()}</span>,
    },
    {
      accessorKey: 'customer',
      header: 'Клиент',
      enableSorting: true
    },
    {
      accessorKey: 'amount',
      header: 'Сумма',
      cell: info => `${info.getValue<number>().toLocaleString('ru-RU')} $`,
      sortingFn: 'basic',
    },
    {
      accessorKey: 'status',
      header: 'Статус',
      cell: info => <StatusBadge status={info.getValue()} />,
      enableSorting: false
    },
    {
      accessorKey: 'createdAt',
      header: 'Дата',
      sortingFn: 'datetime'
    },
  ]

  const table = useReactTable({
    data,
    columns,
    state: {
      sorting,
      globalFilter
    },
    onSortingChange: setSorting,
    onGlobalFilterChange: setGlobalFilter,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    initialState: {
      pagination: {
        pageSize: 25
      }
    },
  })

  return (
    <div>
      <input
        value={globalFilter}
        onChange={e => setGlobalFilter(e.target.value)}
        placeholder='Поиск по всем полям...'
        className='mb-4 w-64 border rounded px-3 py-2'
      />
      <table className='w-full border-collapse'>
        <thead>
          {table.getHeaderGroups().map(headerGroup => (
            <tr key={headerGroup.id}>
              {headerGroup.headers.map(header => (
                <th
                  key={header.id}
                  onClick={header.column.getToggleSortingHandler()}
                  className={header.column.getCanSort() ? 'cursor-pointer select-none' : ''}
                >
                  {flexRender(header.column.columnDef.header, header.getContext())}
                  {{ asc: ' ↑', desc: ' ↓' }[header.column.getIsSorted() as string] ?? ''}
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody>
          {table.getRowModel().rows.map(row => (
            <tr key={row.id} className='hover:bg-gray-50'>
              {row.getVisibleCells().map(cell => (
                <td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
      <div className='flex items-center gap-2 mt-4'>
        <button onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>
          ←
        </button>
        <span>
          Страница {table.getState().pagination.pageIndex + 1} из {table.getPageCount()}
        </span>
        <button onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>
          →
        </button>
        <select
          value={table.getState().pagination.pageSize}
          onChange={e => table.setPageSize(Number(e.target.value))}
        >
          {[10, 25, 50, 100].map(size => (
            <option key={size} value={size}>по {size}</option>
          ))}
        </select>
      </div>
    </div>
  )
}

איך ליישם פיצ'ינג בצד השרת עם React Query?

עבור מערכי נתונים מעל 10,000 שורות, פיצ'ינג בצד הלקוח אינו מקובל — אי אפשר לטעון את כל הנתונים. השתמשו ב-npm install @tanstack/react-table , import { useState } from 'react' import { ColumnDef, flexRender, getCoreRowModel, getSortedRowModel, getPaginationRowModel, getFilteredRowModel, useReactTable, SortingState, } from '@tanstack/react-table' type Order = { id: string customer: string amount: number status: 'pending' | 'paid' | 'cancelled' createdAt: string } function OrdersTable({ data }: { data: Order[] }) { const [sorting, setSorting] = useState<SortingState>([]) const [globalFilter, setGlobalFilter] = useState('') const columns: ColumnDef<Order>[] = [ { accessorKey: 'id', header: '№ заказа', cell: info => <span className='font-mono text-sm'>{info.getValue()}</span>, }, { accessorKey: 'customer', header: 'Клиент', enableSorting: true }, { accessorKey: 'amount', header: 'Сумма', cell: info => `${info.getValue<number>().toLocaleString('ru-RU')} $`, sortingFn: 'basic', }, { accessorKey: 'status', header: 'Статус', cell: info => <StatusBadge status={info.getValue()} />, enableSorting: false }, { accessorKey: 'createdAt', header: 'Дата', sortingFn: 'datetime' }, ] const table = useReactTable({ data, columns, state: { sorting, globalFilter }, onSortingChange: setSorting, onGlobalFilterChange: setGlobalFilter, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), getPaginationRowModel: getPaginationRowModel(), getFilteredRowModel: getFilteredRowModel(), initialState: { pagination: { pageSize: 25 } }, }) return ( <div> <input value={globalFilter} onChange={e => setGlobalFilter(e.target.value)} placeholder='Поиск по всем полям...' className='mb-4 w-64 border rounded px-3 py-2' /> <table className='w-full border-collapse'> <thead> {table.getHeaderGroups().map(headerGroup => ( <tr key={headerGroup.id}> {headerGroup.headers.map(header => ( <th key={header.id} onClick={header.column.getToggleSortingHandler()} className={header.column.getCanSort() ? 'cursor-pointer select-none' : ''} > {flexRender(header.column.columnDef.header, header.getContext())} {{ asc: ' ↑', desc: ' ↓' }[header.column.getIsSorted() as string] ?? ''} </th> ))} </tr> ))} </thead> <tbody> {table.getRowModel().rows.map(row => ( <tr key={row.id} className='hover:bg-gray-50'> {row.getVisibleCells().map(cell => ( <td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td> ))} </tr> ))} </tbody> </table> <div className='flex items-center gap-2 mt-4'> <button onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}> ← </button> <span> Страница {table.getState().pagination.pageIndex + 1} из {table.getPageCount()} </span> <button onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}> → </button> <select value={table.getState().pagination.pageSize} onChange={e => table.setPageSize(Number(e.target.value))}> {[10, 25, 50, 100].map(size => ( <option key={size} value={size}>по {size}</option> ))} </select> </div> </div> ) } , manualPagination וטענו נתונים דרך React Query:

const [{ pageIndex, pageSize }, setPagination] = useState({ pageIndex: 0, pageSize: 25 })
const { data, isFetching } = useQuery({
  queryKey: ['orders', pageIndex, pageSize, sorting, globalFilter],
  queryFn: () => fetchOrders({
    page: pageIndex + 1,
    limit: pageSize,
    sortBy: sorting[0]?.id,
    sortDir: sorting[0]?.desc ? 'desc' : 'asc',
    search: globalFilter
  }),
  keepPreviousData: true,
})
const table = useReactTable({
  data: data?.rows ?? [],
  columns,
  pageCount: data?.pageCount ?? -1,
  state: {
    sorting,
    pagination: { pageIndex, pageSize },
    globalFilter
  },
  manualPagination: true,
  manualSorting: true,
  manualFiltering: true,
  onPaginationChange: setPagination,
})

המפתח הוא הדגלים manualSorting — הם משביתים את הטיפול המובנה, ומעבירים את השליטה לשרת. React Query עם manualFiltering מונע קפיצות בממשק במהלך הטעינה.

השוואת מודלי פיצ'ינג

פרמטר צד הלקוח צד השרת
גודל נתונים עד 10,000 שורות כל כמות
זמן טעינה ראשוני תלוי בגודל מערך הנתונים מיידי (עמוד 1)
סינון בצד הלקוח (אופליין) בצד השרת (אינדקסי SQL)
עומס על השרת טעינה אחת גדולה בקשות קטנות רבות

איך לייצא טבלה ל-CSV ול-Excel?

ייצוא נתונים מסוננים ל-CSV עם BOM עבור כתב קירילי:

function exportToCSV(table: Table<Order>) {
  const headers = table.getAllColumns().filter(col => col.getIsVisible()).map(col => col.columnDef.header as string)
  const rows = table.getFilteredRowModel().rows.map(row => row.getVisibleCells().map(cell => {
    const value = cell.getValue()
    return typeof value === 'string' && value.includes(',') ? `"${value}"` : value
  }))
  const csv = [headers, ...rows].map(r => r.join(',')).join('\n')
  const blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = `orders-${Date.now()}.csv`
  a.click()
  URL.revokeObjectURL(url)
}

במידת הצורך — ייצוא ל-XLSX באמצעות const [{ pageIndex, pageSize }, setPagination] = useState({ pageIndex: 0, pageSize: 25 }) const { data, isFetching } = useQuery({ queryKey: ['orders', pageIndex, pageSize, sorting, globalFilter], queryFn: () => fetchOrders({ page: pageIndex + 1, limit: pageSize, sortBy: sorting[0]?.id, sortDir: sorting[0]?.desc ? 'desc' : 'asc', search: globalFilter }), keepPreviousData: true, }) const table = useReactTable({ data: data?.rows ?? [], columns, pageCount: data?.pageCount ?? -1, state: { sorting, pagination: { pageIndex, pageSize }, globalFilter }, manualPagination: true, manualSorting: true, manualFiltering: true, onPaginationChange: setPagination, }) . אנו מגדירים פורמט תאים, מיזוג וסגנונות.

ווירטואליזציה עבור 100,000+ שורות

שימו לב: כשיש הרבה שורות אבל הפיצ'ינג הוא בצד הלקוח — ווירטואליזציה של שורות דרך manual* מציגה רק את השורות הנראות:

import { useVirtualizer } from '@tanstack/react-virtual'

const tableContainerRef = useRef<HTMLDivElement>(null)
const { rows } = table.getRowModel()
const rowVirtualizer = useVirtualizer({
  count: rows.length,
  getScrollElement: () => tableContainerRef.current,
  estimateSize: () => 48,
  overscan: 10,
})

10,000 שורות נטענות בכ-20 אלפיות השנייה — רק אזור התצוגה נמצא ב-DOM.

מה כלול בעבודה

  1. ניתוח מבנה הנתונים וה-API, בחירת מודל פיצ'ינג.
  2. עיצוב הגדרת עמודות, מיון, מסננים.
  3. יישום באמצעות TanStack Table עם אינטגרציה למערכת העיצוב.
  4. הגדרת פיצ'ינג בצד השרת, ייצוא, ווירטואליזציה.
  5. תיעוד שימוש ותמיכה.

לוחות זמנים: טבלה בסיסית עם מיון ופיצ'ינג — יום אחד. עם פיצ'ינג בצד השרת, מסננים וייצוא — 2–3 ימים. ווירטואליזציה מוסיפה 1–2 ימים. אנו מעריכים את הפרויקט — פשוט כתבו לנו. הזמינו פיתוח טבלה סוהר — קבלו פתרון מוכן תוך יום אחד. קבלו ייעוץ על אינטגרציה של TanStack Table בפרויקט שלכם.

אנחנו עוסקים בפיתוח פרונטאנד כבר 5+ שנים, והשלמנו 50+ פרויקטים עם טבלאות. אנו משתמשים בטכנולוגיות מודרניות: React, TypeScript, TanStack Table, React Query. התוצאה — טבלאות מהירות וגמישות שלא גמגמות על נתונים גדולים. חיסכון ברישוי בהשוואה ל-AG Grid יכול להגיע ל-40% מהתקציב.

אחריות — אנו מתקנים באגים בחינם תוך 30 יום מהמסירה. צרו קשר — נעריך את הפרויקט שלכם תוך יום אחד.