логотип PurpleSchool
логотип PurpleSchool

Работа с таблицами во Vue через TanStack

Автор

Олег Марков

Введение

Работа с таблицами – одна из частых задач при разработке интерфейсов во Vue. Обычно нужно не просто отрисовать данные в таблице, а добавить сортировку, фильтрацию, пагинацию, группировку, работу с большими массивами данных и гибкую настройку интерфейса. TanStack Table – современная библиотека для управления таблицами, которая дает все эти возможности. Причем, она работает как headless (без готового UI), что идеально подходит для проектов на Vue, где вы управляете разметкой полностью самостоятельно.

Здесь вы научитесь подключать и использовать TanStack Table во Vue, создавать кастомные таблицы, добавлять сортировку, фильтрацию и другие важные возможности. Я покажу практические примеры и объясню, как все устроено под капотом, чтобы вы могли делать свои интерфейсы легкими, производительными и удобными в поддержке.


Установка TanStack Table и интеграция с Vue

Начнем с базовой установки и создания первого примера.

Как установить TanStack Table

Вот как добавляется TanStack Table в проект на Vue 3 (TypeScript поддерживается по умолчанию):

npm install @tanstack/vue-table

Если вы работаете с Vue 2, используйте отдельную версию или следуйте документации TanStack Table для Legacy проектов.

Создание базовой таблицы

Давайте начнем с простого примера: таблица, которая просто отображает список объектов.

1. Подготовка данных

Сначала определим наши данные и колонки. Обычно это массивы объектов:

// Пример данных
const data = [
  { id: 1, name: 'Иван', age: 25 },
  { id: 2, name: 'Ольга', age: 34 },
  { id: 3, name: 'Петр', age: 28 },
]

Колонки задаются объектами с описанием, что мы хотим показывать и как:

const columns = [
  {
    header: 'Имя',
    accessorKey: 'name', // Связываем с ключом объекта
  },
  {
    header: 'Возраст',
    accessorKey: 'age',
  },
]

2. Объявление таблицы через TanStack

Теперь подключим таблицу с помощью фабрики:

import { useVueTable, createColumnHelper } from '@tanstack/vue-table' 

// Здесь я использую useVueTable — основной хук для создания таблицы
const table = useVueTable({
  data,
  columns,
})

Это создает объект table, который содержит все необходимые методы и свойства для работы с таблицей.

3. Вывод таблицы в шаблоне

А теперь разметка для вывода таблицы (в SFC .vue файле):

<template>
  <table>
    <thead>
      <tr>
        <th v-for="header in table.getHeaderGroups()[0].headers" :key="header.id">
          {{ header.column.columnDef.header }}
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row in table.getRowModel().rows" :key="row.id">
        <td v-for="cell in row.getVisibleCells()" :key="cell.id">
          {{ cell.getValue() }}
        </td>
      </tr>
    </tbody>
  </table>
</template>

Что здесь важно:

  • getHeaderGroups() – для получения групп хедеров (можно использовать для сложных заголовков).
  • getRowModel().rows – предоставляет строки таблицы.
  • row.getVisibleCells() – доступ к ячейкам строки, которые вы выбрали для отображения.

Кастомизация колонок и ячеек

TanStack Table очень гибкая. Вы можете не просто выводить значения, а полностью кастомизировать вывод через render-функции.

Как сделать "смарт"-ячейки

Посмотрите пример – будет добавлен столбец "Действия" со своей кнопкой:

const columns = [
  {
    header: 'Имя',
    accessorKey: 'name',
  },
  {
    header: 'Возраст',
    accessorKey: 'age',
  },
  {
    header: 'Действия',
    // Кастомный рендерer для ячейки
    cell: ({ row }) => {
      return `<button @click="alert('ID: ' + ${row.original.id})">Показать ID</button>`
    }
  }
]

Но для Vue мы не можем напрямую класть шаблон в строку. Решение — использовать scoped slots или возвращать функцию, а внутри компонента решать, что рендерить.

В типовом варианте TanStack Table во Vue предлагает использовать слоты для сложных столбцов.


Сортировка данных в таблице

Чтобы пользователи могли сортировать данные, достаточно добавить соответствующий плагин при инициализации таблицы и нажимать по заголовку столбца.

Подключение сортировки

  1. Подключаем модуль сортировки:
import { useVueTable, getSortedRowModel, getCoreRowModel } from '@tanstack/vue-table'

const table = useVueTable({
  data,
  columns,
  getCoreRowModel: getCoreRowModel(),
  getSortedRowModel: getSortedRowModel(), // Включаем сортировку
})
  1. Добавляем обработчик сортировки по клику:
<th
  v-for="header in table.getHeaderGroups()[0].headers"
  :key="header.id"
  @click="header.column.toggleSorting()"
>
  {{ header.column.columnDef.header }}
  <!-- Показываем иконку направления сортировки -->
  <span v-if="header.column.getIsSorted()">
    {{ header.column.getIsSorted() === 'asc' ? '▲' : '▼' }}
  </span>
</th>

Комментирую: при каждом клике таблица будет менять порядок сортировки по выбранному столбцу.


Фильтрация данных

Фильтрация — еще один стандартный кейс работы с таблицами. TanStack Table поддерживает быстрые фильтры и сложные функции.

Как добавить фильтрацию

  1. Импортируем фильтрационную функцию:
import { getFilteredRowModel } from '@tanstack/vue-table'
  1. Инициализируем таблицу с фильтрацией:
const table = useVueTable({
  data,
  columns,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
})
  1. Создаем bind для значений фильтра:
<input
  v-model="globalFilter"
  placeholder="Фильтр по всем столбцам"
/>
  1. Передаем значение фильтра в таблицу:
const globalFilter = ref('')

watch(globalFilter, (value) => {
  table.setGlobalFilter(value)
})

Поясняю: теперь при наборе текста будут обновляться данные в таблице.

Фильтрация по отдельному столбцу

Многие хотят фильтровать столбцы выборочно. TanStack Table поддерживает фильтры на уровне столбцов — просто добавьте фильтруемую функцию в определение столбца или используйте стандартные фильтры (например, "contains", "startsWith" и так далее).


Пагинация (Постраничная навигация)

Когда у вас большие объемы данных, то без пагинации не обойтись. Хорошая новость — TanStack Table дает полный контроль.

Пример добавления пагинации

  1. Импортируем и используем пагинационный плагин:
import { getPaginationRowModel } from '@tanstack/vue-table'

const table = useVueTable({
  data,
  columns,
  getCoreRowModel: getCoreRowModel(),
  getPaginationRowModel: getPaginationRowModel(),
})
  1. Добавляем кнопки для перехода между страницами:
<button @click="table.previousPage()" :disabled="!table.getCanPreviousPage()">Назад</button>
<button @click="table.nextPage()" :disabled="!table.getCanNextPage()">Вперед</button>
<span>
  Страница {{ table.getState().pagination.pageIndex + 1 }} из {{ table.getPageCount() }}
</span>

Комментарий: методы previousPage() и nextPage() управляют страницами моделей таблицы.


Группировка данных и поддерево (SubRows)

Иногда нужно выводить иерархические, вложенные данные. TanStack Table отлично поддерживает группировку и вложенность строк.

Как добавить вложенные строки

Давайте рассмотрим ситуацию, где у каждой строки может быть массив "детей":

const data = [
  {
    id: 1,
    name: 'Иван',
    children: [
      { id: 4, name: 'Иван Младший', age: 7 }
    ]
  },
  // …
]

Теперь указываем в таблице функцию получения подстрок:

const table = useVueTable({
  data,
  columns,
  getSubRows: row => row.children, // Подсказываю таблице, где искать подстроки
})

В шаблоне добавляем рекурсивный вывод строк:

<tr v-for="row in table.getRowModel().rows" :key="row.id">
  <td v-for="cell in row.getVisibleCells()" :key="cell.id">
    {{ cell.getValue() }}
  </td>
  <!-- Здесь рекурсивно рендерим потомков -->
  <template v-if="row.getIsExpanded()">
    <tr v-for="subRow in row.getSubRows()" :key="subRow.id">
      <!-- Аналогично выводим содержимое -->
    </tr>
  </template>
</tr>

Для управления раскрытием строк используйте row.toggleExpanded() или row.getIsExpanded().


Ленивая подгрузка (виртуализация) и работа с большими данными

Для таблицы с тысячами строк важно быстро отображать только видимые данные. TanStack Table интегрируется с библиотеками виртуализации (например, @tanstack/vue-virtual).

Основы виртуализации таблицы

  1. Установите виртуализатор:
npm install @tanstack/vue-virtual
  1. Импортируйте и используйте RowVirtualizer:
import { useVirtualizer } from '@tanstack/vue-virtual'
  1. Примените виртуализацию к tbody:
<tbody
  ref="tbody"
  :style="{ height: virtualizer.getTotalSize() + 'px', position: 'relative' }"
>
  <tr
    v-for="virtualRow in virtualizer.getVirtualItems()"
    :key="virtualRow.index"
    :style="{ position: 'absolute', top: virtualRow.start + 'px' }"
  >
    <!-- Выводите ячейки по примеру выше -->
  </tr>
</tbody>

Поясняю: эта схема рендерит только те строки, которые попадут во viewport, экономя ресурсы браузера на больших массивах.


Кастомизация стилей и интеграция с UI библиотеками

TanStack Table не предоставляет готовый UI — здесь вы сами выбираете, как стилизовать и организовать дизайн. Это помогает:

  • Интегрировать таблицу в любой UI-фреймворк (например, Vuetify, Element Plus, PrimeVue)
  • Добавить свои компоненты (например, выпадающие меню, чекбоксы, инпуты, кастомные сортировщики и фильтры).

Используйте слоты Vue и условные конструкции для расширения функционала без ограничения возможностями готового компонента.


Лучшие практики и советы

  • Используйте key-prop в v-for только для уникальных идентификаторов.
  • Храните данные таблицы отдельно от компонентного состояния (например, через Pinia или Vuex) — так будет проще обновлять и масштабировать интерфейс.
  • Минимизируйте количество рендеров при изменении данных — используйте виртуализацию для больших таблиц.
  • Для сложных фильтров и сортировок реализуйте кастомные функции в определении колонки через параметры sortType и filterFn.
  • Пользуйтесь типами данных (TypeScript) — TanStack Table полностью их поддерживает.

Заключение

TanStack Table дает мощный набор инструментов для работы с таблицами во Vue — от простых статических таблиц до сложных динамических интерфейсов с фильтрацией, сортировкой, пагинацией и поддержкой больших объемов данных. Вы полностью управляете разметкой и внешним видом, а библиотека заботится о бизнес-логике работы таблицы. Это позволяет легко создавать кастомные и производительные таблицы для любых задач, не ограничиваясь готовыми решениями.


Частозадаваемые технические вопросы по теме статьи и ответы на них

Как сделать редактируемые ячейки в TanStack Table во Vue?

Чтобы реализовать редактируемые ячейки, используйте scoped slots или возвращайте функции/компоненты в cell-рендере столбца. Для каждого cell используйте v-if для переключения между режимом вывода и редактирования. Для управления значениями используйте v-model и обработчики событий для изменения данных.

Можно ли подключить TanStack Table к серверной пагинации/фильтрации?

Да, просто реализуйте соответствующие обработчики на стороне сервера. Вынесите свойство data в computed или watch, чтобы при изменениях параметров пагинации, фильтрации или сортировки запрашивать новые данные с сервера и обновлять их в таблице через setData().

Как сделать drag-n-drop колонок или строк?

TanStack Table не включает drag-n-drop «из коробки», но позволяет вставлять любые UI-библиотеки для drag-n-drop в вашу обёртку и обновлять порядок колонок (например, через обновление массива columns).

Как реализовать закрепление (freezing) колонок или строк?

В TanStack Table есть колонки с параметрами pin: 'left' или pin: 'right'. Для закрепления используйте эти параметры в описании columns и стилизуйте закрепленные столбцы через CSS (например, position sticky).

Что делать при ошибке "Cannot read property 'headers' of undefined"?

Проверьте, что переданы корректные данные и колонки, а таблица инициализирована до первого рендера шаблона. Обычно ошибка указывает на отсутствие или неправильную структуру columns/data. Используйте проверки и ensure before rendering.

Стрелочка влевоИнтеграция Tiptap для создания редакторов на VueИнструкция по установке и компонентам Vue sliderСтрелочка вправо

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueРабота с teleport для управления DOM во VueПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиРуководство по nextTick для работы с DOMСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний