Привет. Недавно пришлось повозиться с @tanstack/vue-table. Задача была стандартная: нужна таблица с сортировкой, фильтрами и редактированием ячеек. Казалось бы - идеальное время подключить готовое решение. Но не всё так гладко. Делюсь мыслями, граблями и тем, как я в итоге выкрутился.

Что это вообще такое
Если коротко: это headless-библиотека. То есть она даёт логику, а как это будет выглядеть - решаешь ты сам. Никаких готовых тем, никаких дефолтных стилей. Полный контроль, но и вся ответственность на тебе.
Звучит круто, правда? Пока не начнёшь разбираться.
Первое разочарование: документация
Открываешь доки после Quasar или AG Grid - и как будто попал в другой мир. Информация разбросана, примеры часто для React, а про Vue - как будто в последнюю очередь дописали.
И это не паранойя. Библиотека писалась под React, а Vue-версия - это своего рода «порт». Из-за этого некоторые вещи чувствуются чужеродно. Например, хук useVueTable ведёт себя так, будто ожидает, что данные будут неизменяемыми (immutable), а во Vue 3 мы привыкли мутировать объекты через прокси.
Главная проблема: реактивность и мутации
Во Vue ты можешь просто сделать list.push(...) - и интерфейс обновится. В мире TanStack так не работает. Библиотека отслеживает изменения по ссылке на объект. Если ссылка не поменялась - для неё ничего не произошло.
Что это значит на практике:
Добавил элемент через
push()- таблица не увидела.Поменял поле в объекте - ячейка не перерендерилась через flexRender.
Хочешь редактирование «на лету»? Готовься к костылям.
Люди уже наступали на эти грабли:
Как я это обошёл (рабочий подход)
Вместо того чтобы бороться с библиотекой, я принял правила игры: данные для таблицы - только через замену ссылки. И вынес эту логику в отдельный хук.
Вот упрощённый пример того, как я организую работу с данными:
// useTableDataSync.ts
import { shallowRef, watch } from 'vue'
export function useTableDataSync<T extends { id: string | number }>(
source: () => T[],
idField: keyof T = 'id'
) {
const tableData = shallowRef<T[]>([...source()])
watch(source, (next) => {
// Быстрый путь: разная длина - точно есть изменения
if (next.length !== tableData.value.length) {
tableData.value = [...next]
return
}
// Сравниваем по ключу, чтобы не триггерить лишние обновления
const changed = next.some((item, i) => {
const prev = tableData.value[i]
return !prev || item[idField] !== prev[idField]
})
if (changed) {
tableData.value = [...next]
}
}, { deep: true })
return tableData
}А в компоненте использую так:
<script setup lang="ts" generic="T extends { id: string | number }">
import { computed } from 'vue'
import { useVueTable, getCoreRowModel } from '@tanstack/vue-table'
import { useTableDataSync } from './useTableDataSync'
const props = defineProps<{
rows: T[]
columns: ColumnDef<T>[]
rowKey?: keyof T
}>()
const data = useTableDataSync(() => props.rows, props.rowKey ?? 'id')
const table = useVueTable({
get data() { return data.value },
columns: props.columns,
getRowId: (row) => String(row[props.rowKey ?? 'id']),
getCoreRowModel: getCoreRowModel(),
})
</script>Так я изолировал «неудобную» часть и больше не думаю о мутациях внутри компонента.

Про FlexRender: зачем он?
Честно - не понял. FlexRender пытается абстрагировать рендер ячеек, но во Vue это только усложняет. Автокомплит перестаёт работать, типы «плывут», а в шаблонах начинается магия с h() и component :is.
Я просто рендерю таблицу обычным v-for. Для кастомных ячеек использую слоты - так понятнее, типизируется лучше и команда не страдает.
Когда стоит брать TanStack Table
Не буду говорить, что библиотека плохая. Она мощная. Но подходит не всем.
Бери, если:
Нужна сложная логика: серверная сортировка, пагинация, группировка.
Готов потратить время на настройку и обёртки.
В команде есть люди, которые уже работали с TanStack.
Не бери, если:
Нужна простая таблица «показать данные».
Хочется быстро сделать и забыть.
Команда не готова разбираться с особенностями реактивности.
Мой чеклист перед подключением
Попробуй сделать таблицу на чистом Vue за час. Если получилось - зачем усложнять?
Проверь, нужна ли тебе вся эта мощь. Часто 80% функционала достигается обычными хуками.
Если всё же берёшь TanStack - сразу выдели «прослойку» для синхронизации данных. Не смешивай бизнес-логику с логикой таблицы.
Тестируй редактирование ячеек на раннем этапе. Именно там вылезают самые противные баги.
Таким образом :)
@tanstack/vue-table - это как спортивный автомобиль: круто, быстро, но если дорога разбитая - лучше взять внедорожник. Библиотека даёт контроль, но требует понимания её внутренней кухни.
Я не жалею, что попробовал. Теперь лучше понимаю, где стоит писать своё, а где можно взять готовое. Главное - не гнаться за модными решениями, а смотреть на задачу и команду.





















