https://github.com/vatesfr/flexi-table
https://github.com/vatesfr/flexi-table
Last synced: 27 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/vatesfr/flexi-table
- Owner: vatesfr
- License: mit
- Created: 2026-06-19T09:20:20.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-06-19T15:19:38.000Z (about 1 month ago)
- Last Synced: 2026-06-19T15:29:33.093Z (about 1 month ago)
- Language: TypeScript
- Size: 93.8 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: SECURITY.md
Awesome Lists containing this project
README
# flexi-table
[](https://github.com/vatesfr/flexi-table/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/@vates/flexi-table-react)
[](https://www.npmjs.com/package/@vates/flexi-table-vue)
[](https://www.npmjs.com/package/@vates/flexi-table-vanilla)
[](https://opensource.org/licenses/MIT)
A flexible, fully-typed data table for React, Vue 3, and vanilla JS — with sorting, filtering, column visibility, and row grouping built in.
## Live demo
[React demo](https://vatesfr.github.io/flexi-table/react/) · [Vue demo](https://vatesfr.github.io/flexi-table/vue/) · [Vanilla demo](https://vatesfr.github.io/flexi-table/vanilla/)
## Packages
| Package | Description |
| -------------------------------------------------- | ---------------------------------- |
| [`@vates/flexi-table-react`](./packages/react) | React component and hook |
| [`@vates/flexi-table-vue`](./packages/vue) | Vue 3 component and composable |
| [`@vates/flexi-table-vanilla`](./packages/vanilla) | Vanilla JS, no framework required |
| [`@vates/flexi-table-core`](./packages/core) | Framework-agnostic logic (pure TS) |
## Features
- Multi-column sort
- Value checklist filters and numeric range filters
- Column visibility toggle
- Row grouping (grouped column hides from the table automatically)
- Row selection with checkboxes — select all (across pages), group selection, indeterminate state
- Client-side pagination
- i18n via a `labels` prop — defaults to English, with built-in locales for FR, ES, DE, PT
- Custom cell rendering via render props (React), scoped slots (Vue), or `format` string functions (vanilla)
- Fully typed with TypeScript generics (`TRow extends object`)
## Quick start
### React
```bash
npm install @vates/flexi-table-react
```
```tsx
import { DataTable, type ColumnDef } from '@vates/flexi-table-react'
interface User {
id: number
name: string
role: string
salary: number
}
const COLUMNS: ColumnDef[] = [
{ key: 'name', label: 'Name', type: 'string' },
{ key: 'role', label: 'Role', type: 'string', groupable: true },
{
key: 'salary',
label: 'Salary',
type: 'number',
format: (v) => Number(v).toLocaleString() + ' €',
},
]
export default function App() {
return
}
```
Custom cell rendering with render props:
```tsx
{ key: 'role', label: 'Role', type: 'string',
render: (value, row) => ,
renderFilterLabel: value => }
```
### Vue
```bash
npm install @vates/flexi-table-vue
```
```vue
import { DataTable, type ColumnDef } from '@vates/flexi-table-vue'
interface User {
id: number
name: string
role: string
salary: number
}
const COLUMNS: ColumnDef<User>[] = [
{ key: 'name', label: 'Name', type: 'string' },
{ key: 'role', label: 'Role', type: 'string', groupable: true },
{
key: 'salary',
label: 'Salary',
type: 'number',
format: (v) => Number(v).toLocaleString() + ' €',
},
]
```
### Vanilla JS
```bash
npm install @vates/flexi-table-vanilla
```
```ts
import { createFlexiTable, type ColumnDef } from '@vates/flexi-table-vanilla'
const COLUMNS: ColumnDef[] = [
{ key: 'name', label: 'Name', type: 'string' },
{ key: 'role', label: 'Role', type: 'string', groupable: true },
{
key: 'salary',
label: 'Salary',
type: 'number',
format: (v) => Number(v).toLocaleString() + ' €',
},
]
const table = createFlexiTable(document.getElementById('table')!, {
data: users,
columns: COLUMNS,
rowKey: 'id',
})
// Update later
table.setData(newUsers)
table.destroy()
```
CSS is injected automatically into ``. Cell output is string-only — use `format` to control rendering.
## i18n
All UI strings are in English by default. Use a built-in locale or supply any overrides via the `labels` prop:
```tsx
import { LABELS_FR } from '@vates/flexi-table-react' // or -vue or -vanilla
```
Built-in locales: `LABELS_EN` (default), `LABELS_FR`, `LABELS_ES`, `LABELS_DE`, `LABELS_PT`.
You can also pass a `Partial` to override individual strings — it is shallow-merged over the default English labels.
## Theming
All colors are CSS custom properties (`--color-background-primary`, `--color-text-primary`, etc.). Dark mode activates automatically via `prefers-color-scheme: dark` and can be forced with `data-theme="dark"` or `data-theme="light"` on any ancestor element.
**Vanilla** — tokens and dark-mode rules are injected automatically. No setup required.
**React / Vue** — define the tokens in your own global stylesheet. See the [vanilla README](./packages/vanilla/README.md#theming) for the full token list and default values.
```js
// Force dark / light / follow OS preference
document.documentElement.dataset.theme = 'dark'
document.documentElement.dataset.theme = 'light'
delete document.documentElement.dataset.theme
```
## Column definition
```ts
interface ColumnDefBase {
key: keyof TRow & string // must be a key of TRow
label: string
type?: 'string' | 'number' | 'date' // controls filter UI; default: 'string'
width?: number
format?: (value: unknown) => string // plain-string formatter (both adapters)
sortable?: boolean // default: true
filterable?: boolean // default: true
groupable?: boolean // default: false
}
```
React extends this with `render?` and `renderFilterLabel?`. Vue uses scoped slots instead.
## Development
See [CONTRIBUTING.md](./CONTRIBUTING.md).
## License
MIT