Skip to content

vatesfr/flexi-table

Repository files navigation

flexi-table

CI npm npm npm License: 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 · Vue demo · Vanilla demo

Packages

Package Description
@vates/flexi-table-react React component and hook
@vates/flexi-table-vue Vue 3 component and composable
@vates/flexi-table-vanilla Vanilla JS, no framework required
@vates/flexi-table-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

npm install @vates/flexi-table-react
import { DataTable, type ColumnDef } from '@vates/flexi-table-react'

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() + ' €',
  },
]

export default function App() {
  return <DataTable data={users} columns={COLUMNS} rowKey="id" />
}

Custom cell rendering with render props:

{ key: 'role', label: 'Role', type: 'string',
  render: (value, row) => <Badge label={String(value)} />,
  renderFilterLabel: value => <Badge label={value} /> }

Vue

npm install @vates/flexi-table-vue
<script setup lang="ts">
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() + '',
  },
]
</script>

<template>
  <DataTable :data="users" :columns="COLUMNS" row-key="id">
    <template #cell-role="{ value }">
      <Badge :label="String(value)" />
    </template>
    <template #filter-role="{ value }">
      <Badge :label="value" />
    </template>
    <template #group-role="{ value }">
      <Badge :label="String(value)" />
    </template>
  </DataTable>
</template>

Vanilla JS

npm install @vates/flexi-table-vanilla
import { createFlexiTable, type ColumnDef } from '@vates/flexi-table-vanilla'

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() + ' €',
  },
]

const table = createFlexiTable(document.getElementById('table')!, {
  data: users,
  columns: COLUMNS,
  rowKey: 'id',
})

// Update later
table.setData(newUsers)
table.destroy()

CSS is injected automatically into <head>. 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:

import { LABELS_FR } from '@vates/flexi-table-react' // or -vue or -vanilla

<DataTable labels={LABELS_FR} ... />

Built-in locales: LABELS_EN (default), LABELS_FR, LABELS_ES, LABELS_DE, LABELS_PT.

You can also pass a Partial<DataTableLabels> to override individual strings — it is shallow-merged over the default English labels.

Column definition

interface ColumnDefBase<TRow extends object> {
  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.

License

MIT

About

No description, website, or topics provided.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors