flex-table-vue
v0.1.9
Published
A flexible, feature-rich Vue 3 data table component with sorting, virtual scrolling, and interactive column management. Ships a pre-built CSS bundle (flex-table-vue/style.css): Tailwind v4 utilities compiled in-repo, then @layer blocks flattened for compa
Maintainers
Readme
flex-table-vue
A flexible, feature-rich Vue 3 data table component with sorting, virtual scrolling, and interactive column management. Styles ship as a pre-built CSS file (Tailwind compiled in-repo); fully typed with TypeScript.
Repository: github.com/stenlitims/flexTable · npm: flex-table-vue
Features
- Sorting — client-side and server-side sorting with configurable sort direction
- Virtual scrolling — efficient rendering of large datasets with configurable row height and buffer
- Column settings modal — drag-and-drop reordering, per-column visibility toggle, and width controls
- LocalStorage persistence — user preferences (column order, visibility, width) saved per table via
tableId - i18n-ready — all UI text labels customizable via the
textsprop - Bundled styles — import
flex-table-vue/style.cssfor all table/modal utilities (Tailwind is compiled inside the package; no need to add this library to your Tailwindcontentpaths) - Named slots —
header-{value}andcell-{value}slots for custom rendering - TypeScript — complete type definitions included
- Vue plugin — register globally with
app.use()
Installation
npm install flex-table-vueRequirements
- Vue 3.5+
- Styling: import
flex-table-vue/style.cssonce (includes Tailwind Preflight and every utility class used by the components). You do not need Tailwind configured in the host app for the built-in table UI to look correct. - If the host app already uses Tailwind, you can keep your own setup for layouts and slot content; you typically do not need to add
node_modules/flex-table-vueto Tailwindcontentfor the library’s own markup. - Column reordering in the settings modal uses SortableJS, bundled in
flex-table-vue.
Tailwind / PostCSS in the host app
From v0.1.8 onward, style.css is post-processed so @layer blocks are removed (rules stay the same). That avoids errors when the host app’s Tailwind v3 / PostCSS pipeline re-parses the file (e.g. @layer base is used but no matching @tailwind base). You can still import flex-table-vue/style.css like any global stylesheet.
Usage
Basic
<script setup>
import { ref } from 'vue'
import { FlexTable } from 'flex-table-vue'
import 'flex-table-vue/style.css'
const headers = [
{ text: 'Name', value: 'name', sortable: true, width: 200 },
{ text: 'Email', value: 'email', sortable: true, width: 250 },
{ text: 'Role', value: 'role', sortable: true, width: 120 },
]
const items = [
{ id: 1, name: 'Alice', email: '[email protected]', role: 'Admin' },
{ id: 2, name: 'Bob', email: '[email protected]', role: 'User' },
{ id: 3, name: 'Charlie', email: '[email protected]', role: 'Editor' },
]
const sort = ref({ by: undefined, dir: 'asc' })
</script>
<template>
<FlexTable
:headers="headers"
:items="items"
v-model:sort-table="sort"
table-id="users-table"
/>
</template>Global Registration
import { createApp } from 'vue'
import FlexTablePlugin from 'flex-table-vue'
import 'flex-table-vue/style.css'
import App from './App.vue'
const app = createApp(App)
app.use(FlexTablePlugin)
app.mount('#app')After registration, use <FlexTable> anywhere without importing.
Custom Cell Rendering
Use named slots cell-{value} to customize cell content:
<FlexTable :headers="headers" :items="items">
<template #cell-name="{ item }">
<strong>{{ item.name }}</strong>
</template>
<template #cell-role="{ item }">
<span class="badge">{{ item.role }}</span>
</template>
</FlexTable>Custom Header Rendering
Use named slots header-{value} to customize header content:
<FlexTable :headers="headers" :items="items">
<template #header-name="{ header }">
<i class="icon-user" /> {{ header.text }}
</template>
</FlexTable>Server-Side Sorting
<script setup>
const sort = ref({ by: 'name', dir: 'asc' })
function onServerSort(newSort) {
// Fetch data from API with sort params
fetchItems({ sortBy: newSort.by, sortDir: newSort.dir })
}
</script>
<template>
<FlexTable
:headers="headers"
:items="items"
v-model:sort-table="sort"
:server-sort="true"
@server-sort="onServerSort"
/>
</template>Virtual Scrolling
For large datasets (1000+ rows), enable virtual scrolling:
<FlexTable
:headers="headers"
:items="largeDataset"
:virtual-scroll="true"
row-height="36"
virtual-scroll-max-height="70vh"
:virtual-scroll-buffer="5"
/>i18n — Custom Text Labels
All text labels can be customized via the texts prop:
<FlexTable
:headers="headers"
:items="items"
:texts="{
emptyText: 'Немає даних',
columnSettingsTitle: 'Налаштування колонок',
showAllText: 'Показати всі',
hideAllText: 'Приховати всі',
resetWidthsText: 'Скинути ширину',
resetAllText: 'Скинути налаштування',
closeText: 'Закрити',
noColumnsText: 'Немає колонок',
widthLabel: 'px',
}"
/>API Reference
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| headers | TableHeader[] | [] | Column definitions (required) |
| items | Record<string, unknown>[] | [] | Row data (required) |
| sortTable | TableSort | { by: undefined, dir: 'asc' } | Current sort state (supports v-model) |
| tableId | string | '' | Unique ID for localStorage persistence |
| keyRow | string | 'id' | Property name used as row key |
| serverSort | boolean | false | Disable client-side sorting, emit server-sort instead |
| virtualScroll | boolean | false | Enable virtual scrolling |
| rowHeight | number | 36 | Row height in pixels (for virtual scroll) |
| virtualScrollMaxHeight | string | '70vh' | Max height of scrollable container |
| virtualScrollBuffer | number | 5 | Extra rows rendered above/below viewport |
| texts | FlexTableTexts | {} | Custom text labels for i18n |
TableHeader
interface TableHeader {
text: string // Display name
value: string // Data property key
width?: number // Column width in pixels (default: 100)
sortable?: boolean // Enable sorting for this column
visible?: boolean // Initial visibility (default: true)
}TableSort
interface TableSort {
by?: string // Sort field name
dir?: 'asc' | 'desc' // Sort direction
}FlexTableTexts
| Prop | Default | Description |
|------|---------|-------------|
| emptyText | 'No data' | Text shown when table has no items |
| columnSettingsTitle | 'Column Settings' | Title of the column settings modal |
| showAllText | 'Show all' | Button text to show all columns |
| hideAllText | 'Hide all' | Button text to hide all columns |
| resetWidthsText | 'Reset widths' | Button text to reset column widths |
| resetAllText | 'Reset all' | Button text to reset all settings |
| closeText | 'Close' | Close button text |
| noColumnsText | 'No columns to configure' | Text when no columns available |
| widthLabel | 'px' | Width unit label |
Events
| Event | Payload | Description |
|-------|---------|-------------|
| update:sortTable | TableSort | Sort state changed (for v-model) |
| update:headers | TableHeader[] | Headers changed (visibility, width, order) |
| rowClick | Record<string, unknown> | Row was clicked |
| server-sort | TableSort | Sort requested (only when serverSort is true) |
Slots
| Slot | Props | Description |
|------|-------|-------------|
| header-{value} | { header: TableHeader } | Custom header cell content |
| cell-{value} | { item: Record, header: TableHeader } | Custom body cell content |
License
MIT
