A zero-dependency Vue 3 datatable component with client/server-side pagination, sorting, filtering, row selection, and custom cell rendering.
Document & Demos
npm install @bhplugin/vue3-datatable --save
yarn add @bhplugin/vue3-datatable
< template >
< vue3-datatable :rows ="rows " :columns ="cols " :sortable ="true " :columnFilter ="true "> </ vue3-datatable >
</ template >
< script setup lang ="ts ">
import { ref } from "vue" ;
import Vue3Datatable from "@bhplugin/vue3-datatable" ;
import type { IColumnDefinition } from "@bhplugin/vue3-datatable" ;
import "@bhplugin/vue3-datatable/dist/style.css" ;
const cols = ref < IColumnDefinition [ ] > ( [
{ field : "id" , title : "ID" , isUnique : true , type : "number" , filter : false } ,
{ field : "name" , title : "Name" } ,
{ field : "email" , title : "Email" } ,
{ field : "age" , title : "Age" , type : "number" } ,
{ field : "dob" , title : "Date of Birth" , type : "date" } ,
{ field : "isActive" , title : "Active" , type : "bool" } ,
{ field : "address.city" , title : "City" } ,
] ) ;
const rows = ref < Array < Record < string , unknown > >> ( [
{ id : 1 , name : "John Doe" , email : "john@example.com" , age : 28 , dob : "1997-05-15" , isActive : true , address : { city : "New York" } } ,
{ id : 2 , name : "Jane Smith" , email : "jane@example.com" , age : 34 , dob : "1991-11-22" , isActive : false , address : { city : "London" } } ,
// ...
] ) ;
</ script >
Prop
Type
Default
Description
columns (required)
IColumnDefinition[]
[]
Table column definitions
rows (required)
Record[]
[]
Table row data
isServerMode
boolean
false
Enable server-side mode (disables client-side sort/filter/pagination)
totalRows
number
0
Total row count (required in server mode)
skin
string
"bh-table-striped bh-table-hover"
Table skin classes: bh-table-striped, bh-table-hover, bh-table-bordered, bh-table-compact
loading
boolean
false
Show loading overlay/skeleton
hasCheckbox
boolean
false
Show checkbox column for row selection
search
string
""
Global search string
page
number
1
Current page (1-based)
pageSize
number
10
Rows per page
pageSizeOptions
number[]
[10, 20, 30, 50, 100]
Page size dropdown options
showPageSize
boolean
true
Show page size dropdown
rowClass
string | Function
""
Custom row class (string or (row) => string)
cellClass
string | Function
""
Custom cell class (string or (row) => string)
sortable
boolean
false
Enable column sorting
sortColumn
string
""
Initial sort column field
sortDirection
'asc' | 'desc'
"asc"
Initial sort direction
columnFilter
boolean
false
Enable per-column filter inputs
columnFilterLang
Record<string, string>
null
i18n labels for filter conditions (see below)
pagination
boolean
true
Show pagination
showNumbers
boolean
true
Show page number buttons
showNumbersCount
number
5
Max visible page number buttons
showFirstPage
boolean
true
Show first page button
showLastPage
boolean
true
Show last page button
paginationInfo
string
"Showing {0} to {1} of {2} entries"
Pagination info template ({0} = start, {1} = end, {2} = total)
noDataContent
string
"No data available"
Empty state text
skeletonRowCount
number
10
Number of skeleton rows while loading
stickyHeader
boolean
false
Sticky table header
height
string
"500px"
Scrollable height (only with stickyHeader)
stickyFirstColumn
boolean
false
Sticky first column
cloneHeaderInFooter
boolean
false
Clone header as footer row
selectRowOnClick
boolean
false
Toggle row selection on row click
{
no_filter : 'No filter' ,
contain : 'Contain' , not_contain : 'Not contain' ,
equal : 'Equal' , not_equal : 'Not equal' ,
start_with : 'Starts with' , end_with : 'Ends with' ,
greater_than : 'Greater than' , greater_than_equal : 'Greater than or equal' ,
less_than : 'Less than' , less_than_equal : 'Less than or equal' ,
is_null : 'Is null' , is_not_null : 'Not null' ,
all : 'All' , true : 'True' , false : 'False' // boolean column filter labels
}
Prop
Type
Default
Description
isUnique
boolean
false
Mark as unique row identifier (used for selection keys)
field
string
""
Data field path (supports dot notation: address.city)
title
string
""
Column header text
value
string
""
Initial filter value
condition
IFilterCondition
""
Default filter condition (contain for string, equal for number/date, empty for bool)
type
string
"string"
Column type: string, number, date, bool
width
string
""
Column width (e.g., "200px")
minWidth
string
""
Minimum column width
maxWidth
string
""
Maximum column width
hide
boolean
false
Hide column from rendering
filter
boolean
true
Enable column filter
search
boolean
true
Include in global search
sort
boolean
true
Enable sorting
html
boolean
false
Column contains raw HTML
cellRenderer
Function
-
Custom cell renderer: (row) => '<strong>html</strong>' (sanitized automatically)
headerClass
string
""
Custom header cell class
cellClass
string
""
Custom body cell class
Event
Payload
Description
changeServer
IServerChangeResponse
Server mode only — aggregate event with full state (page, sort, search, filters, change_type)
pageChange
number
User clicked pagination button (silent on programmatic resets)
pageSizeChange
number
Page size dropdown changed
sortChange
ISortChangeResponse
Column sort applied (does NOT reset page)
filterChange
IColumnDefinition[]
Column filter value or condition changed
searchChange
string
Global search prop changed
rowSelect
Record[]
Row selection changed (user interaction only)
rowClick
Record
Row single click
rowDBClick
Record
Row double click
reset
-
reset() called — only event during reset, all others suppressed
Sort does not reset page
Filter/search/pagesize resets to page 1 but only emits the specific event (not pageChange)
pageChange fires only from explicit user pagination clicks
During reset, only reset (+ changeServer in server mode) emits — all other events suppressed
Method
Description
reset()
Reset all state (selection, filters, search, sort, page)
getFilteredRows()
Returns all filtered rows
getVisibleRows()
Returns current page visible rows
getColumnFilters()
Returns column definitions with current filter state
getSelectedRows()
Returns all selected rows
clearSelectedRows()
Deselect all rows
selectRow(index)
Select row by index
unselectRow(index)
Deselect row by index
isRowSelected(index)
Check if row is selected
Custom cell rendering (named by column field)
< vue3-datatable :rows ="rows " :columns ="cols ">
< template #name ="{ value } ">
< strong > {{ value.name }}</ strong >
</ template >
</ vue3-datatable >
Pagination arrow slots
< template #firstArrow > « </ template >
< template #previousArrow > ‹ </ template >
< template #nextArrow > › </ template >
< template #lastArrow > » </ template >
< template #noData >
< div > No records found.</ div >
</ template >
< vue3-datatable
:rows ="rows "
:columns ="cols "
:loading ="loading "
:totalRows ="totalRows "
:isServerMode ="true "
:pageSize ="10 "
:sortable ="true "
:columnFilter ="true "
@page-change ="onPageChange "
@page-size-change ="onPageSizeChange "
@sort-change ="onSortChange "
@filter-change ="onFilterChange "
@search-change ="onSearchChange "
/>
Changelogs
@bhplugin/vue3-datatable is open-sourced software licensed under the MIT license .