Feature-rich Data Table Library For Vue 3

Install & Download:

# Yarn
$ yarn add vue3-datatable
# NPM
$ npm i vue3-datatable

Description:

A feature-rich and highly customizable data table component to manage, display, and interact with large datasets in your Vue projects.

Features:

  • Pagination
  • Filtering
  • Sorting
  • Server-side rendering
  • Sticky table header
  • Row selection
  • Custom actions
  • And much more…

See Also:

How to use it:

1. Install and import the data table component.

import Vue3Datatable from '@bhplugin/vue3-datatable'
import '@bhplugin/vue3-datatable/dist/style.css'

2. Create a static data table in your app.

<script setup lang="ts">
  import { ref } from 'vue';
  import Vue3Datatable from '@bhplugin/vue3-datatable';
  import '@bhplugin/vue3-datatable/dist/style.css';
  const config = useRuntimeConfig();
  onMounted(() => {
    getUsers();
  });
  const loading: any = ref(true);
  const rows: any = ref(null);
  const cols =
    ref([
        { field: 'id', title: 'ID', isUnique: true },
        { field: 'firstName', title: 'First Name' },
        { field: 'lastName', title: 'Last Name' },
        { field: 'email', title: 'Email' },
        { field: 'age', title: 'Age', type: 'number' },
        { field: 'dob', title: 'Birthdate', type: 'date' },
        { field: 'address.city', title: 'City' },
        { field: 'isActive', title: 'Active', type: 'bool' },
    ]) || [];
  const getUsers = async () => {
    try {
      loading.value = true;
      const response = await fetch('/api/user', {
        method: 'POST',
        body: JSON.stringify({ pagesize: 500 }),
      });
      const data = await response.json();
      rows.value = data?.data;
    } catch {}
    loading.value = false;
  };
</script>
<template>
  <div>
    <vue3-datatable :rows="rows" :columns="cols" :loading="loading" :sortable="true" :columnFilter="true"> </vue3-datatable>
  </div>
</template>

3. An advanced example:

import { ref } from 'vue';
import Vue3Datatable from '@bhplugin/vue3-datatable';
import '@bhplugin/vue3-datatable/dist/style.css';
import ApexChart from 'vue3-apexcharts';
const config = useRuntimeConfig();
onMounted(() => {
  getUsers();
});
const loading: any = ref(true);
const total_rows = ref(0);
const params = reactive({
  current_page: 1,
  pagesize: 10,
  sort_column: 'id',
  sort_direction: 'asc',
});
const rows: any = ref(null);
const cols =
  ref([
      { field: 'id', title: 'ID', isUnique: true },
      { field: 'firstName', title: 'User' },
      { field: 'country', title: 'Country', sort: false },
      { field: 'email', title: 'Email' },
      { field: 'age', title: 'Progress', sort: false },
      { field: 'phone', title: 'Phone' },
      { field: 'rating', title: 'Rate', sort: false, minWidth: '120px', headerClass: 'justify-center', cellClass: 'justify-center' },
      { field: 'series', title: 'Progress', sort: false },
      { field: 'status', title: 'Status', sort: false },
  ]) || [];
const getUsers = async () => {
  try {
      loading.value = true;
      const response = await fetch('/api/user', {
          method: 'POST',
          body: JSON.stringify(params),
      });
      const data = await response.json();
      rows.value = data?.data;
      total_rows.value = data?.meta?.total;
  } catch {}
  loading.value = false;
};
const changeServer = (data: any) => {
  params.current_page = data.current_page;
  params.pagesize = data.pagesize;
  params.sort_column = data.sort_column;
  params.sort_direction = data.sort_direction;
  getUsers();
};
const countryList = [
  { code: 'AE', name: 'United Arab Emirates' },
  { code: 'AR', name: 'Argentina' },
  { code: 'AT', name: 'Austria' },
  { code: 'AU', name: 'Australia' },
  { code: 'BE', name: 'Belgium' },
  { code: 'BG', name: 'Bulgaria' },
  { code: 'BN', name: 'Brunei' },
  { code: 'BR', name: 'Brazil' },
  { code: 'BY', name: 'Belarus' },
  { code: 'CA', name: 'Canada' },
  { code: 'CH', name: 'Switzerland' },
  { code: 'CL', name: 'Chile' },
  { code: 'CN', name: 'China' },
  { code: 'CO', name: 'Colombia' },
  { code: 'CZ', name: 'Czech Republic' },
  { code: 'DE', name: 'Germany' },
  { code: 'DK', name: 'Denmark' },
  { code: 'DZ', name: 'Algeria' },
  { code: 'EC', name: 'Ecuador' },
  { code: 'EG', name: 'Egypt' },
  { code: 'ES', name: 'Spain' },
  { code: 'FI', name: 'Finland' },
  { code: 'FR', name: 'France' },
  { code: 'GB', name: 'United Kingdom' },
  { code: 'GR', name: 'Greece' },
  { code: 'HK', name: 'Hong Kong' },
  { code: 'HR', name: 'Croatia' },
  { code: 'HU', name: 'Hungary' },
  { code: 'ID', name: 'Indonesia' },
  { code: 'IE', name: 'Ireland' },
  { code: 'IL', name: 'Israel' },
  { code: 'IN', name: 'India' },
  { code: 'IT', name: 'Italy' },
  { code: 'JO', name: 'Jordan' },
  { code: 'JP', name: 'Japan' },
  { code: 'KE', name: 'Kenya' },
  { code: 'KH', name: 'Cambodia' },
  { code: 'KR', name: 'South Korea' },
  { code: 'KZ', name: 'Kazakhstan' },
  { code: 'LA', name: 'Laos' },
  { code: 'LK', name: 'Sri Lanka' },
  { code: 'MA', name: 'Morocco' },
  { code: 'MM', name: 'Myanmar' },
  { code: 'MO', name: 'Macau' },
  { code: 'MX', name: 'Mexico' },
  { code: 'MY', name: 'Malaysia' },
  { code: 'NG', name: 'Nigeria' },
  { code: 'NL', name: 'Netherlands' },
  { code: 'NO', name: 'Norway' },
  { code: 'NZ', name: 'New Zealand' },
  { code: 'PE', name: 'Peru' },
  { code: 'PH', name: 'Philippines' },
  { code: 'PK', name: 'Pakistan' },
  { code: 'PL', name: 'Poland' },
  { code: 'PT', name: 'Portugal' },
  { code: 'QA', name: 'Qatar' },
  { code: 'RO', name: 'Romania' },
  { code: 'RS', name: 'Serbia' },
  { code: 'RU', name: 'Russia' },
  { code: 'SA', name: 'Saudi Arabia' },
  { code: 'SE', name: 'Sweden' },
  { code: 'SG', name: 'Singapore' },
  { code: 'SK', name: 'Slovakia' },
  { code: 'TH', name: 'Thailand' },
  { code: 'TN', name: 'Tunisia' },
  { code: 'TR', name: 'Turkey' },
  { code: 'TW', name: 'Taiwan' },
  { code: 'UK', name: 'Ukraine' },
  { code: 'UG', name: 'Uganda' },
  { code: 'US', name: 'United States' },
  { code: 'VN', name: 'Vietnam' },
  { code: 'ZA', name: 'South Africa' },
  { code: 'BA', name: 'Bosnia and Herzegovina' },
  { code: 'BD', name: 'Bangladesh' },
  { code: 'EE', name: 'Estonia' },
  { code: 'IQ', name: 'Iraq' },
  { code: 'LU', name: 'Luxembourg' },
  { code: 'LV', name: 'Latvia' },
  { code: 'MK', name: 'North Macedonia' },
  { code: 'SI', name: 'Slovenia' },
  { code: 'PA', name: 'Panama' },
];
const chart_options = computed(() => {
  let option = {
      chart: { sparkline: { enabled: true } },
      stroke: { curve: 'smooth', width: 2 },
      markers: { size: [4, 7], strokeWidth: 0 },
      colors: [randomColor()],
      grid: { padding: { top: 5, bottom: 5 } },
      tooltip: {
          x: { show: false },
          y: {
              title: {
                  formatter: () => {
                      return '';
                  },
              },
          },
      },
  };
  return option;
});
const randomColor = () => {
  const color = ['#5367ff', '#0dcaf0', '#00d09c', '#ff585d', '#ffb61b', '#1da1f2'];
  const random = Math.floor(Math.random() * color.length);
  return color[random];
};
const randomStatusColor = () => {
  const color = ['primary', 'secondary', 'success', 'danger', 'warning', 'info'];
  const random = Math.floor(Math.random() * color.length);
  return color[random];
};
const randomStatus = () => {
  const status = ['PAID', 'APPROVED', 'FAILED', 'CANCEL', 'SUCCESS', 'PENDING', 'COMPLETE'];
  const random = Math.floor(Math.random() * status.length);
  return status[random];
};
const getRandomNumber = (min: number, max: number) => {
  return Math.floor(Math.random() * (max - min + 1)) + min;
};
const getCountry = () => {
  const random = Math.floor(Math.random() * countryList.length);
  return countryList[random];
};
<template>
  <div>
    <vue3-datatable
      :rows="rows"
      :columns="cols"
      :loading="loading"
      :totalRows="total_rows"
      :isServerMode="true"
      :pageSize="params.pagesize"
      :sortable="true"
      :sortColumn="params.sort_column"
      :sortDirection="params.sort_direction"
      class="advanced-table whitespace-nowrap"
      @change="changeServer"
    >
    <template #id="data">
        strong class="text-info">#{{ data.value.id }}</strong>
    </template>
    <template #firstName="data">
      <div class="flex items-center gap-2">
        <img :src="`/assets/images/profile/profile-${getRandomNumber(1, 32)}.jpeg`" class="w-9 h-9 rounded-full max-w-none" alt="user-profile" />
        <div class="font-semibold">{{ data.value.firstName + ' ' + data.value.lastName }}</div>
      </div>
    </template>
    <template #country>
      <div class="flex items-center gap-2">
        <img width="24" :src="`/assets/images/flags/${getCountry().code}.svg`" class="max-w-none" alt="user profile" />
        <div class="text-gray-600">{{ getCountry().name }}</div>
      </div>
    </template>
    <template #email="data">
      <a :href="`mailto:${data.value.email}`" class="text-primary hover:underline">{{ data.value.email }}</a>
    </template>
    <template #age>
      <div class="progress-bar">
        <div class="progress-line" :style="`width:${getRandomNumber(15, 100)}%; background-color:${randomColor()}`"></div>
      </div>
    </template>
      <template #rating="data">
        <div class="flex items-center justify-center text-warning">
          <div v-for="i in getRandomNumber(1, 5)" :key="i + data.value.id">
            <svg
              xmlns="http://www.w3.org/2000/svg"
              width="16"
              height="16"
              viewBox="0 0 24 24"
              fill="#e2a03f"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
              class="feather feather-star f-icon-line">
              <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
           </svg>
          </div>
        </div>
      </template>
      <template #series="data">
        <div style="width: 150px">
          <client-only>
            <apex-chart :key="data.value.id" height="30" type="line" :options="chart_options" :series="[{ data: [21, 9, 36, 12, 44, 25, 59] }]"></apex-chart>
            </client-only>
          </div>
      </template>
      <template #status>
        <span class="badge" :class="[randomStatusColor()]">{{ randomStatus() }}</span>
      </template>
    </vue3-datatable>
  </div>
</template>

4. Available props.

const props = withDefaults(defineProps<Props>(), {
  loading: false,
  isServerMode: false,
  skin: 'bh-table-striped bh-table-hover',
  totalRows: 0,
  rows: () => [],
  columns: () => [],
  hasCheckbox: false,
  search: '',
  columnChooser: false,
  page: 1,
  pageSize: 10,
  pageSizeOptions: () => [10, 20, 30, 50, 100],
  showPageSize: true,
  rowClass: <never>[],
  cellClass: <never>[],
  cellRenderer: null,
  sortable: false,
  sortColumn: 'id',
  sortDirection: 'asc',
  columnFilter: false,
  pagination: true,
  showNumbers: true,
  showNumbersCount: 5,
  showFirstPage: true,
  showLastPage: true,
  firstArrow: '',
  lastArrow: '',
  nextArrow: '',
  previousArrow: '',
  paginationInfo: 'Showing {0} to {1} of {2} entries',
  noDataContent: 'No data available',
  stickyHeader: false,
  height: '500px',
  stickyFirstColumn: false,
  cloneHeaderInFooter: false,
  selectRowOnClick: false,
});

Preview:

Feature-rich Data Table Library For Vue 3

Changelog:

v1.1.3 (11/07/2023)

  • fix global search issue on latest vue version

Add Comment