Skip to content

Table

A Vue UI kit component for Baklava's bl-table component for displaying tabular data. Supports columns, data, sorting, row selection, loading/empty states, pagination, title/header actions, and per-column custom slots.

Basic Usage

Provide columns and data to render a table. Rows should include an id when using selectable; otherwise the row index is used.

NameAgeEmailJohn Doe30john@example.comJane Smith25jane@example.comBob Wilson35bob@example.com
vue
<template>
  <BvTable :columns="columns" :data="tableData" />
</template>

<script setup>
import { BvTable } from "@baklavue/ui";

const columns = [
  { key: "name", label: "Name" },
  { key: "age", label: "Age" },
  { key: "email", label: "Email" },
];

const tableData = [
  { id: 1, name: "John Doe", age: 30, email: "john@example.com" },
  { id: 2, name: "Jane Smith", age: 25, email: "jane@example.com" },
  { id: 3, name: "Bob Wilson", age: 35, email: "bob@example.com" },
];
</script>

Sortable

Enable column sorting with the sortable prop. Set sortable: true on individual columns to allow sorting by that column.

NameAgeEmailJohn Doe30john@example.comJane Smith25jane@example.comBob Wilson35bob@example.com
vue
<template>
  <BvTable :columns="columns" :data="tableData" :sortable="true" />
</template>

<script setup>
import { BvTable } from "@baklavue/ui";

const columns = [
  { key: "name", label: "Name", sortable: true },
  { key: "age", label: "Age", sortable: true },
  { key: "email", label: "Email" },
];

const tableData = [
  { id: 1, name: "John Doe", age: 30, email: "john@example.com" },
  { id: 2, name: "Jane Smith", age: 25, email: "jane@example.com" },
  { id: 3, name: "Bob Wilson", age: 35, email: "bob@example.com" },
];
</script>

Loading State

Use isLoading to show a loading state with a spinner and custom text.

Loading data...
vue
<template>
  <BvTable
    :columns="columns"
    :data="tableData"
    :is-loading="isLoading"
    loading-text="Loading data..."
  />
</template>

<script setup>
import { ref } from "vue";
import { BvTable } from "@baklavue/ui";

const isLoading = ref(true);
const columns = [...];
const tableData = [...];
</script>

Empty State

When data is empty, use the empty-state slot to customize the content shown.

No data found
vue
<template>
  <BvTable :columns="columns" :data="tableData">
    <template #empty-state>
      <span>No data found</span>
    </template>
  </BvTable>
</template>

<script setup>
import { BvTable } from "@baklavue/ui";

const columns = [...];
const tableData = [];
</script>

Pagination

Pass a pagination object to enable pagination. Listen to @change for page or items-per-page changes.

Users
NameAgeEmailJohn Doe30john@example.comJane Smith25jane@example.comBob Wilson35bob@example.comAlice Brown28alice@example.comCharlie Davis42charlie@example.com
vue
<template>
  <BvTable
    :columns="columns"
    :data="tableData"
    title="Users"
    :pagination="pagination"
    @change="onPageChange"
  />
</template>

<script setup>
import { ref, computed } from "vue";
import { BvTable } from "@baklavue/ui";

const currentPage = ref(1);
const itemsPerPage = ref(10);

const pagination = computed(() => ({
  currentPage: currentPage.value,
  totalItems: 50,
  itemsPerPage: itemsPerPage.value,
  hasJumper: true,
  jumperLabel: "Go to page",
  hasSelect: true,
  selectLabel: "Items per page",
  itemsPerPageOptions: [
    { text: "5 Items", value: 5 },
    { text: "10 Items", value: 10 },
    { text: "25 Items", value: 25 },
  ],
}));

const onPageChange = (event) => {
  const { selectedPage, itemsPerPage: newItemsPerPage } = event.detail;
  currentPage.value = selectedPage;
  itemsPerPage.value = newItemsPerPage;
};
</script>

Column Slots

Use scoped slots named by column key to customize cell content. Each slot receives { row, value }.

NameStatusEmailJohn Doeactivejohn@example.comJane Smithinactivejane@example.comBob Wilsonactivebob@example.com
vue
<template>
  <BvTable :columns="columns" :data="tableData">
    <template #status="{ value }">
      <span
        :style="{
          color: value === 'active' ? 'var(--bl-color-success)' : 'var(--bl-color-neutral-600)',
          fontWeight: 500,
        }"
      >
        {{ value }}
      </span>
    </template>
  </BvTable>
</template>

<script setup>
import { BvTable } from "@baklavue/ui";

const columns = [
  { key: "name", label: "Name" },
  { key: "status", label: "Status" },
  { key: "email", label: "Email" },
];

const tableData = [
  { id: 1, name: "John Doe", status: "active", email: "john@example.com" },
  { id: 2, name: "Jane Smith", status: "inactive", email: "jane@example.com" },
];
</script>

Selectable

Enable row selection with the selectable prop. Use @select to handle selection changes. When selectable is true, rows should have an id for reliable selection keys.

vue
<template>
  <BvTable
    :columns="columns"
    :data="tableData"
    :selectable="true"
    :multiple="true"
    v-model:selected="selectedIds"
    @select="onSelect"
  />
</template>

<script setup>
import { ref } from "vue";
import { BvTable } from "@baklavue/ui";

const columns = [
  { key: "name", label: "Name" },
  { key: "age", label: "Age" },
];

const tableData = [
  { id: 1, name: "John", age: 30 },
  { id: 2, name: "Jane", age: 25 },
];

const selectedIds = ref([]);

const onSelect = (event) => {
  console.log("Selected:", event.detail);
};
</script>

Title and Header Actions

Use the title prop and header-actions slot for a header bar above the table.

vue
<template>
  <BvTable title="Users" :columns="columns" :data="tableData">
    <template #header-actions>
      <BvButton>Add User</BvButton>
    </template>
  </BvTable>
</template>

Props

PropTypeDefaultDescription
titlestringundefinedOptional title above the table
headerOptionsobjectundefined{ sticky?: boolean; minCellWidth?: string }
dataTableRow[][]Table data rows
columnsTableColumn[]undefinedColumn definitions
sortablebooleanundefinedEnable column sorting
selectablebooleanundefinedEnable row selection
multiplebooleanundefinedEnable multiple row selection
selected(string | number)[]undefinedSelected row keys (v-model:selected)
sortKeystringundefinedSort key for sorted column
sortDirectionstringundefinedSort direction: '' | 'asc' | 'desc'
stickyFirstColumnbooleanundefinedMake first column sticky
stickyLastColumnbooleanundefinedMake last column sticky
isLoadingbooleanundefinedShow loading state
paginationTablePaginationPropsundefinedPagination configuration
loadingTextstring"Loading..."Text shown in loading state

Events

EventPayloadDescription
row-clickCustomEventEmitted when a row is clicked
sortCustomEventEmitted when sort options change
selectCustomEvent<string[]>Emitted when selected rows change
changeCustomEvent<PaginationDetail>Emitted when pagination changes (selectedPage, prevPage, itemsPerPage)

Slots

SlotPropsDescription
header-actions-Right side of title bar
empty-state-Content when data.length === 0
[column.key]{ row, value }Per-column cell content (scoped)

Types

typescript
import type {
  TableProps,
  TableColumn,
  TableRow,
  TablePaginationProps,
} from "@baklavue/ui";

interface TableColumn {
  key: string;
  label?: string;
  name?: string;
  sortable?: boolean;
}

type TableRow = Record<string, unknown> & { id?: string | number };

interface TablePaginationProps {
  currentPage: number;
  totalItems: number;
  itemsPerPage: number;
  hasJumper?: boolean;
  jumperLabel?: string;
  hasSelect?: boolean;
  selectLabel?: string;
  itemsPerPageOptions?: { text: string; value: number }[];
}

interface TableProps {
  title?: string;
  headerOptions?: { sticky?: boolean; minCellWidth?: string };
  data?: TableRow[];
  columns?: TableColumn[];
  sortable?: boolean;
  selectable?: boolean;
  multiple?: boolean;
  selected?: (string | number)[];
  sortKey?: string;
  sortDirection?: string;
  stickyFirstColumn?: boolean;
  stickyLastColumn?: boolean;
  isLoading?: boolean;
  pagination?: TablePaginationProps;
  loadingText?: string;
}

Usage Notes

  • Column keys: Each column's key must match a property in the data rows. Use label or name for header text.
  • Row ids: When selectable is true, rows should have an id (string or number) for reliable selection. Falls back to row index if absent.
  • Sortable columns: Set sortable: true on the table and optionally on individual columns.
  • Empty state: Provide custom content via the empty-state slot (e.g., icon, message, action button).