npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sketchmonk/datatable-vue

v0.1.0-alpha.2

Published

A headless data table component for vue 3

Readme

DataTable Vue

NPM Version

A headless Vue 3 DataTable component library that provides powerful data table functionality while giving you complete control over the UI.

Introduction

The DataTable Vue library offers a collection of headless components that handle complex data table functionality, such as:

  • Sorting
  • Filtering
  • Pagination
  • Row selection
  • Column configuration

The library provides the logic and state management while giving you complete freedom to design your own UI. This separation of concerns allows you to create highly customizable data tables without being limited by predefined styles or markup.

Installation

npm install @sketchmonk/datatable-vue

or

yarn add @sketchmonk/datatable-vue

Basic Usage

<script setup>
import { 
  DataTable, 
  DataTableHeader, 
  DataTableBody, 
  DataTableRow, 
  DataTableCell 
} from '@sketchmonk/datatable-vue';
import { ref, computed } from 'vue';

// Define your columns
const columns = [
  {
    key: 'name',
    title: 'Name',
    accessor: (row) => row.name,
    sortable: true
  },
  {
    key: 'email',
    title: 'Email',
    accessor: (row) => row.email
  }
];

// Sample data
const sampleData = [
  { id: 1, name: 'John Doe', email: '[email protected]' },
  { id: 2, name: 'Jane Smith', email: '[email protected]' }
];

// State for the data table
const pagination = ref({ page: 1, perPage: 10 });
const filters = ref({});
const sort = ref(null);
const selection = ref([]);

// Computed properties for the data
const total = computed(() => sampleData.length);
const tableData = computed(() => sampleData);
</script>

<template>
  <DataTable
    :columns="columns"
    :data="tableData"
    :total="total"
    v-model:pagination="pagination"
    v-model:filters="filters"
    v-model:sort="sort"
    v-model:selection="selection"
    :identity="(row) => row.id"
  >
    <table>
      <DataTableHeader v-slot="{ columns }">
        <thead>
          <tr>
            <th v-for="column in columns" :key="column.key">
              {{ column.def.title }}
            </th>
          </tr>
        </thead>
      </DataTableHeader>
      <DataTableBody v-slot="{ rows }">
        <tbody>
          <DataTableRow v-for="row in rows" :key="row.id" :row="row" v-slot="{ cells }">
            <tr>
              <DataTableCell v-for="cell in cells" :key="cell.column.key" :cell="cell">
                <td>{{ cell.value }}</td>
              </DataTableCell>
            </tr>
          </DataTableRow>
        </tbody>
      </DataTableBody>
    </table>
  </DataTable>
</template>

Components API

DataTable

The root component that provides the data table context to all child components.

Props

| Prop | Type | Required | Description | | ---------- | ------------------------------ | -------- | ------------------------------------------------ | | columns | DataTableColumnDef<D>[] | Yes | Array of column definitions | | data | D[] | Yes | Array of data items to display | | total | number | Yes | Total number of items (for pagination) | | identity | (row: D) => string \| number | Yes | Function to get a unique identifier for each row |

Models

| Model | Type | Default | Description | | ------------ | ---------------------------- | -------------------------- | ------------------------ | | filters | DataTableFilterState | {} | Current filter state | | sort | DataTableSortState \| null | null | Current sort state | | pagination | DataTablePaginationState | { page: 1, perPage: 10 } | Current pagination state | | selection | D[] | [] | Currently selected rows |

Slots

| Slot | Props | Description | | ------- | ----- | ----------------------------- | | default | - | The content of the data table |

DataTableHeader

Provides context for the table header.

Slots

| Slot | Props | Description | | ------- | ------------- | --------------------------------------------------- | | default | { columns } | Content of the table header, with access to columns |

DataTableHeading

Provides context for a table column heading.

Props

| Prop | Type | Required | Description | | -------- | ------------------------ | -------- | ------------------ | | column | DataTableColumnInfo<D> | Yes | Column information |

Slots

| Slot | Props | Description | | ------- | ------------------------ | --------------------------------------------------------------- | | default | DataTableColumnInfo<D> | Content of the column heading with access to column information |

DataTableBody

Provides context for the table body.

Slots

| Slot | Props | Description | | ------- | ---------- | --------------------------------------------- | | default | { rows } | Content of the table body with access to rows | | empty | - | Content to display when there are no rows |

DataTableRow

Provides context for a table row.

Props

| Prop | Type | Required | Description | | ----- | --------------------- | -------- | --------------- | | row | DataTableRowInfo<D> | Yes | Row information |

Slots

| Slot | Props | Description | | ------- | ------------------------- | ----------------------------------------------------------- | | default | Row props and { cells } | Content of the row with access to row information and cells |

DataTableCell

Provides context for a table cell.

Props

| Prop | Type | Required | Description | | ------ | ---------------------- | -------- | ---------------- | | cell | DataTableCellInfo<D> | Yes | Cell information |

Slots

| Slot | Props | Description | | ------- | ---------- | --------------------------------------------------- | | default | Cell props | Content of the cell with access to cell information |

DataTablePageSelector

Provides selection functionality for the current page.

Slots

| Slot | Props | Description | | ------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | | default | { checked, indeterminate, onChange, onToggle, isAllSelected, isSomeSelected, selectAll, unselectAll } | Content with access to page selection state and methods |

DataTableRowSelector

Provides selection functionality for a specific row.

Slots

| Slot | Props | Description | | ------- | ------------------------------------------------- | ------------------------------------------------------ | | default | { value, onChange, onToggle, select, unselect } | Content with access to row selection state and methods |

DataTablePagination

Provides pagination functionality.

Slots

| Slot | Props | Description | | ------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------- | | default | { from, to, total, totalPages, page, perPage, onPageChange, onPerPageChange, hasNextPage, hasPrevPage, onNext, onPrev, onLast, onFirst } | Content with access to pagination state and methods |

Types

DataTableColumnDef<D, M=any>

interface DataTableColumnDef<D, M=any> {
  key: string;                   // Unique column identifier
  title: VNode | string;         // Column display title
  accessor: (row: D, index: number) => any;  // Function to access data
  sortable?: boolean;            // Whether the column is sortable
  filterable?: boolean;          // Whether the column is filterable
  meta?: M;                      // Optional metadata for the column
}

DataTablePaginationState

interface DataTablePaginationState {
  page: number;      // Current page number
  perPage: number;   // Items per page
}

DataTableSortState

interface DataTableSortState {
  field: string;     // Column key to sort by
  order: "asc" | "desc";  // Sort direction
}

DataTableFilterState

interface DataTableFilterState {
  [key: string]: any;  // Key-value pairs of filter values
}

Advanced Example

The following example demonstrates a more advanced implementation with sorting, selection, and pagination:

<script setup>
import {
  DataTable,
  DataTableBody,
  DataTableCell,
  DataTableHeader,
  DataTableHeading,
  DataTablePageSelector,
  DataTablePagination,
  DataTableRow,
  DataTableRowSelector,
} from '@sketchmonk/datatable-vue';
import { ref, computed } from 'vue';

// Define your columns
const columns = [
  {
    key: 'name',
    title: 'Name',
    accessor: (row) => row.name,
    sortable: true
  },
  {
    key: 'email',
    title: 'Email',
    accessor: (row) => row.email,
    sortable: true
  },
  {
    key: 'status',
    title: 'Status',
    accessor: (row) => row.active ? 'Active' : 'Inactive',
    filterable: true
  }
];

// Sample data
const data = [
  { id: 1, name: 'John Doe', email: '[email protected]', active: true },
  { id: 2, name: 'Jane Smith', email: '[email protected]', active: false },
  // ... more data
];

// State for the data table
const pagination = ref({ page: 1, perPage: 10 });
const filters = ref({});
const sort = ref(null);
const selection = ref([]);

// Computed properties
const total = computed(() => data.length);
const tableData = computed(() => {
  // In a real application, you might fetch data from an API here
  // using the pagination, filters, and sort values
  return data.slice(
    (pagination.value.page - 1) * pagination.value.perPage,
    pagination.value.page * pagination.value.perPage
  );
});
</script>

<template>
  <DataTable
    :columns="columns"
    :data="tableData"
    :total="total"
    v-model:pagination="pagination"
    v-model:filters="filters"
    v-model:sort="sort"
    v-model:selection="selection"
    :identity="(row) => row.id"
  >
    <div class="data-table">
      <table>
        <DataTableHeader v-slot="{ columns }">
          <thead>
            <tr>
              <DataTablePageSelector v-slot="{ checked, indeterminate, onChange }">
                <th>
                  <input
                    type="checkbox"
                    :checked="checked"
                    :indeterminate="indeterminate"
                    @change="onChange($event.target.checked)"
                  />
                </th>
              </DataTablePageSelector>
              <DataTableHeading
                v-for="column in columns"
                :key="column.key"
                :column="column"
                v-slot="columnProps"
              >
                <th>
                  <div class="heading-content">
                    <span>{{ column.def.title }}</span>
                    <button
                      v-if="column.def.sortable"
                      @click="columnProps.toggleSort"
                      class="sort-button"
                    >
                      {{ columnProps.sort === 'asc' ? '↑' : columnProps.sort === 'desc' ? '↓' : '↕' }}
                    </button>
                  </div>
                </th>
              </DataTableHeading>
            </tr>
          </thead>
        </DataTableHeader>
        <DataTableBody v-slot="{ rows }">
          <tbody>
            <DataTableRow
              v-for="row in rows"
              :key="row.id"
              :row="row"
              v-slot="{ cells }"
            >
              <tr :class="{ selected: row.isSelected }">
                <td>
                  <DataTableRowSelector v-slot="{ value, onChange }">
                    <input
                      type="checkbox"
                      :checked="value"
                      @change="onChange($event.target.checked)"
                    />
                  </DataTableRowSelector>
                </td>
                <DataTableCell
                  v-for="cell in cells"
                  :key="cell.column.key"
                  :cell="cell"
                >
                  <td>{{ cell.value }}</td>
                </DataTableCell>
              </tr>
            </DataTableRow>
          </tbody>
        </DataTableBody>
      </table>
      <DataTablePagination v-slot="paginationProps">
        <div class="pagination">
          <div>
            Showing {{ paginationProps.from }} to {{ paginationProps.to }} of {{ paginationProps.total }} entries
          </div>
          <div class="pagination-controls">
            <button
              @click="paginationProps.onFirst"
              :disabled="!paginationProps.hasPrevPage"
            >
              First
            </button>
            <button
              @click="paginationProps.onPrev"
              :disabled="!paginationProps.hasPrevPage"
            >
              Previous
            </button>
            <span>
              Page {{ paginationProps.page }} of {{ paginationProps.totalPages }}
            </span>
            <button
              @click="paginationProps.onNext"
              :disabled="!paginationProps.hasNextPage"
            >
              Next
            </button>
            <button
              @click="paginationProps.onLast"
              :disabled="!paginationProps.hasNextPage"
            >
              Last
            </button>
          </div>
        </div>
      </DataTablePagination>
    </div>
  </DataTable>
</template>

<style>
.data-table {
  width: 100%;
}
table {
  width: 100%;
  border-collapse: collapse;
}
thead th {
  background: #f1f1f1;
  padding: 8px;
  text-align: left;
}
.heading-content {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.sort-button {
  background: none;
  border: none;
  cursor: pointer;
}
tbody td {
  padding: 8px;
  border-bottom: 1px solid #ddd;
}
tr.selected {
  background-color: #e6f7ff;
}
.pagination {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 16px;
}
.pagination-controls {
  display: flex;
  gap: 8px;
  align-items: center;
}
</style>

Composables

For advanced customization, the library provides several composables:

  • useDataTable<D>(): Access the data table context
  • useDataTableRow<D>(): Access the current row context
  • useDataTableCell<D>(): Access the current cell context
  • useDataTableColumn<D>(): Access the current column context

These composables can be used within custom components to access the data table state and methods.

Extending with Custom Components

You can create custom components that integrate with the DataTable by using the provided composables. For example, to create a custom cell renderer:

<script setup>
import { useDataTableCell } from '@sketchmonk/datatable-vue';

const cell = useDataTableCell();
</script>

<template>
  <div class="custom-cell">
    <span class="label">{{ cell.column.def.title }}:</span>
    <strong>{{ cell.value }}</strong>
  </div>
</template>

License

MIT