STable
Таблица.
В отличие от популярных библиотек компонентов для Vue3:
- Семантика поддерживает и итератор строк-значений, и просто вставку строк как есть.
- Позволяет задать состояние, когда нет данных, одним атрибутом.
- Позволяет сделать горизонтальный скролл сверху таблицы, что полезно для длинных таблиц с большим кол-вом колонок.
Базовый пример
| Пользователь | Тариф | Баланс | Роль | Дата регистрации | |
| Ivanov | Базовый | 3000 | customer | 2025-11-04 | |
| Stepanov | Базовый | 4500 | customer | 2025-11-05 | |
| Petrov | Базовый | 1716 | customer | 2025-11-05 | |
| ИТОГО | 9216 |
<STable :data="users">
<template #header>
<td>Пользователь</td>
<td>Тариф</td>
<td>Баланс</td>
<td>Роль</td>
<td>Дата регистрации</td>
<td></td>
</template>
<template #row="{ row }">
<td>{{ row.username }}</td>
<td>{{ row.plan }}</td>
<td>{{ row.balance }}</td>
<td>{{ row.role }}</td>
<td>{{ row.created_at }}</td>
<td>
<SActionIcon title="Удалить" :confirm="`Вы действительно хотите удалить пользователя «${row.username}»?`"
@click="deleteUser(row.username)" icon="trash" danger />
</td>
</template>
<template #footer>
<td>ИТОГО</td>
<td></td>
<td>{{ totalBalance }}</td>
<td></td>
<td></td>
<td></td>
</template>
</STable>Утилиты выравнивания
По умолчанию ячейки выравниваются по позиции колонки: первая — по левому краю, последняя — по правому, остальные — по центру (одинаково для ячеек <td> и заголовков <th>). Выравнивание конкретной ячейки можно переопределить классами на <td>/<th> — .left, .center, .right, а .nowrap запрещает перенос строки:
| Товар | Цена | Остаток | Артикул |
| Кофемолка | 3 200 ₽ | 12 | KF-001-XL |
| Чайник | 1 850 ₽ | 4 | CH-204 |
<STable :data="rows">
<template #header>
<td>Товар</td>
<td class="right">Цена</td>
<td class="left">Остаток</td>
<td class="nowrap">Артикул</td>
</template>
<template #row="{ row }">
<td>{{ row.name }}</td>
<td class="right">{{ row.price }}</td>
<td class="left">{{ row.stock }}</td>
<td class="nowrap">{{ row.sku }}</td>
</template>
</STable>Подсветка строк при наведении
Для подсветки при наведении добавляем атрибут hoverable
| Пользователь | Тариф | Баланс | Роль | Дата регистрации | |
| Ivanov | Базовый | 3000 | customer | 2025-11-04 | |
| Stepanov | Базовый | 4500 | customer | 2025-11-05 | |
| Petrov | Базовый | 1716 | customer | 2025-11-05 | |
| ИТОГО | 9216 |
<STable :data="users" hoverable>
...
</STable>Явные границы по краям ячеек
Для отрисовки границ добавляем атрибут bordered
| Пользователь | Тариф | Баланс | Роль | Дата регистрации | |
| Ivanov | Базовый | 3000 | customer | 2025-11-04 | |
| Stepanov | Базовый | 4500 | customer | 2025-11-05 | |
| Petrov | Базовый | 1716 | customer | 2025-11-05 | |
| ИТОГО | 9216 |
<STable :data="users" bordered>
...
</STable>Выделение четных строк
Для выделения четных строк добавляем атрибут striped
| Пользователь | Тариф | Баланс | Роль | Дата регистрации | |
| Ivanov | Базовый | 3000 | customer | 2025-11-04 | |
| Stepanov | Базовый | 4500 | customer | 2025-11-05 | |
| Petrov | Базовый | 1716 | customer | 2025-11-05 | |
| Sidorov | Базовый | 6000 | customer | 2025-11-06 | |
| ИТОГО | 15216 |
<STable :data="users" striped>
...
</STable>Компактные ячейки
По умолчанию у ячеек комфортные отступы. Для плотных таблиц с большим количеством данных можно уменьшить отступы атрибутом compact.
| Пользователь | Тариф | Баланс | Роль | Дата регистрации | |
| Ivanov | Базовый | 3000 | customer | 2025-11-04 | |
| Stepanov | Базовый | 4500 | customer | 2025-11-05 | |
| Petrov | Базовый | 1716 | customer | 2025-11-05 | |
| ИТОГО | 9216 |
<STable :data="users" compact>
...
</STable>Фиксированная шапка
Чтобы зафиксировать шапку, устанавливаем высоту таблицы в атрибуте height.
| Пользователь | Имя | Фамилия | Дата рождения | Рост | Вес | Тариф | Баланс | Роль | Дата регистрации | |
| Ivanov | Иван | Иванов | 1995.06.06 | 175 | 80 | Базовый | 3000 | Пользователь | 2025-11-04 | |
| Stepanov | Степан | Степанов | 1990.02.14 | 182 | 88 | - | 4500 | Админ | 2025-11-05 | |
| Petrov | Петр | Петров | 1988.09.21 | 178 | 76 | Премиум | 1716 | Пользователь | 2025-11-05 | |
| Sidorov | Сергей | Сидоров | 1993.12.01 | 170 | 72 | Базовый | 6000 | Пользователь | 2025-11-06 | |
| Alexeev | Алексей | Алексеев | 1996.03.30 | 185 | 90 | - | 2000 | Редактор | 2025-11-09 | |
| Ivanov2 | Иван | Иванов | 1995.06.06 | 175 | 80 | Базовый | 3000 | Пользователь | 2025-11-04 | |
| Stepanov2 | Степан | Степанов | 1990.02.14 | 182 | 88 | - | 4500 | Админ | 2025-11-05 | |
| Petrov2 | Петр | Петров | 1988.09.21 | 178 | 76 | Премиум | 1716 | Пользователь | 2025-11-05 | |
| Sidorov2 | Сергей | Сидоров | 1993.12.01 | 170 | 72 | Базовый | 6000 | Пользователь | 2025-11-06 | |
| Alexeev2 | Алексей | Алексеев | 1996.03.30 | 185 | 90 | - | 2000 | Редактор | 2025-11-09 | |
| ИТОГО | 34432 |
<STable :data="users" height="300px">
...
</STable>80vh, чтобы таблица занимала 80% от высоты экрана. Горизонтальный скролл сверху
| Пользователь | Имя | Фамилия | Дата рождения | Рост | Вес | Тариф | Баланс | Роль | Дата регистрации | |
| Ivanov | Иван | Иванов | 1995.06.06 | 175 | 80 | Базовый | 3000 | Пользователь | 2025-11-04 | |
| Stepanov | Степан | Степанов | 1990.02.14 | 182 | 88 | - | 4500 | Админ | 2025-11-05 | |
| Petrov | Петр | Петров | 1988.09.21 | 178 | 76 | Премиум | 1716 | Пользователь | 2025-11-05 | |
| Sidorov | Сергей | Сидоров | 1993.12.01 | 170 | 72 | Базовый | 6000 | Пользователь | 2025-11-06 | |
| Alexeev | Алексей | Алексеев | 1996.03.30 | 185 | 90 | - | 2000 | Редактор | 2025-11-09 | |
| ИТОГО | 17216 |
<STable :data="users" top-scroll>
...
</STable>Кастомный контент внутри tbody
Если нужно задать явные строки в tbody вместо перебора #row, то используем дефолтный слот (в нем для отдельных строк нужно явно указать <tr>).
| Пользователь | Тариф | Баланс | Роль | Дата регистрации | |
| Ivanov | Базовый | 3000 | customer | 2025-11-04 | |
| Stepanov | Базовый | 4500 | customer | 2025-11-05 | |
| Petrov | Базовый | 1716 | customer | 2025-11-05 | |
| ИТОГО | 9216 |
<STable>
<template #header>
<td>Пользователь</td>
<td>Тариф</td>
<td>Баланс</td>
<td>Роль</td>
<td>Дата регистрации</td>
<td></td>
</template>
<tr v-for="user in users" :key="user.username">
<td>{{ user.username }}</td>
<td>{{ user.plan }}</td>
<td>{{ user.balance }}</td>
<td>{{ user.role }}</td>
<td>{{ user.created_at }}</td>
<td>
<SActionIcon title="Удалить" :confirm="`Вы действительно хотите удалить пользователя «${user.username}»?`"
@click="deleteUser(user.username)" icon="trash" danger />
</td>
</tr>
<template #footer>
<td>ИТОГО</td>
<td></td>
<td>{{ totalBalance }}</td>
<td></td>
<td></td>
<td></td>
</template>
</STable>Несколько строк в header / footer
Если это нужно, просто используем отдельные слоты headers / footers (добавляется в конце "s"), в которые уже добавляем <tr>.
<template>
<STable>
<template #headers>
<tr><td>...</td></tr>
<tr><td>...</td></tr>
</template>
...
<template #footers>
<tr><td>...</td></tr>
<tr><td>...</td></tr>
</template>
</STable>
</template>Сообщение о том, что нет данных
Когда задаем data и перебираем его через <template #row>, то часто (например, когда есть фильтры страницы) бывает нужно показать состояние «Ничего не найдено», когда в data ноль строк. По умолчанию в таком случае выводится сообщение «Ничего не найдено», которое можно заменить на кастомное через пропс/слот nodata:
| Пользователь | Тариф | Баланс | Роль | Дата регистрации | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Пользователи не найдены | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ИТОГО | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<STable :data="users" nodata="Пользователи не найдены">
...
</STable>или
| Пользователь | Тариф | Баланс | Роль | Дата регистрации | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
По заданным критериям поиска ничего не нашлось | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ИТОГО | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<STable :data="users">
<template #nodata>
<SNote>По заданным критериям поиска ничего не нашлось</SNote>
</template>
...
</STable>Интерфейс компонента
Свойства (Props)
| Название | Тип | По умолчанию | Описание |
|---|---|---|---|
| data | T[] | Record<string | number, T> | undefined | Массив или объект данных для итерации (используется вместе со слотом #row). |
| hoverable | boolean | false | Подсветка строки при наведении курсора. |
| striped | boolean | false | Чередование цвета фона строк (выделение четных строк). |
| bordered | boolean | false | Отрисовка границ (бордеров) у всех ячеек. |
| compact | boolean | false | Уменьшенные отступы в ячейках для плотных таблиц. |
| nodata | string | 'Ничего не найдено' | Текст, выводимый, когда data пустое. |
| fixedHeader | boolean | false | Фиксированная шапка (принудительно), если не задана height. |
| height | string | undefined | Фиксированная высота таблицы (например, 300px или 80vh). Автоматически включает fixedHeader. |
| topScroll | boolean | false | Переносит горизонтальный скролл наверх таблицы. |
Слоты (Slots)
| Название | Описание |
|---|---|
| header | Содержимое <tr/> внутри <thead>. Подходит для большинства таблиц (без rowspan/colspan в шапке). |
| headers | Полный контроль над <thead> (вместо header). Нужно явно писать теги <tr>. |
| row | Слот с областью видимости { row, index }. Отрисовывается для каждого элемента из data. Внутри нужно писать только <td>. |
| default | Переопределяет содержимое <tbody>. Используется, если не передан data. Нужно явно писать теги <tr>. |
| footer | Содержимое <tr/> внутри <tfoot>. |
| footers | Полный контроль над <tfoot> (содержит теги <tr>). |
| nodata | Кастомный HTML для пустого состояния, заменяет пропс nodata. |