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 🙏

© 2025 – Pkg Stats / Ryan Hefner

zuno-grid-vue

v1.5.0

Published

A powerful and feature-rich data grid component / responsive table for Vue 3.

Readme

📊 ZunoGrid

<ZunoGrid /> is a powerful and flexible grid component for Vue.
It supports flat and hierarchical (tree) data, sorting, resizing, pagination, virtual scrolling, and row selection.


📦 Installation & Quick Start

1️⃣ Installation

Install the package using your preferred package manager:

npm install @sanjaym/zuno-grid-vue
# or
yarn add @sanjaym/zuno-grid-vue

2️⃣ Plugin Registration

Register the ZunoGrid plugin globally in your application's entry file (e.g., main.ts).
This makes the <ZunoGrid /> component available everywhere.

main.ts

import { createApp } from "vue";
import App from "./App.vue";

import ZunoGridPlugin from "@sanjaym/zuno-grid-vue";
import "@sanjaym/zuno-grid-vue/dist/style.css"; // Import the default styles

const app = createApp(App);

app.use(ZunoGridPlugin);

app.mount("#app");

3️⃣ Quick Start

Use the component in your application.
You only need to provide the rows and columns props.

MyGridComponent.vue

<template>
  <div style="height: 400px; width: 100%;">
    <ZunoGrid :rows="rows" :columns="columns" row-id="id" />
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import type { GridColumn, GridRow } from "@sanjaym/zuno-grid-vue";

const columns = ref<GridColumn[]>([
  { key: "name", header: "Name", width: 250 },
  { key: "email", header: "Email" }, // Width auto-calculated
  { key: "status", header: "Status", width: 150, sortable: true },
]);

const rows = ref<GridRow[]>([
  { id: 1, name: "John Doe", email: "[email protected]", status: "Active" },
  {
    id: 2,
    name: "Jane Smith",
    email: "[email protected]",
    status: "Inactive",
  },
  // ... more rows
]);
</script>

🚀 Props API

🔹 Grid Props

Configure the grid's behavior using these props:

| Prop | Type | Default | Description | | -------------------- | ----------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------- | | rows | GridRow[] | [] | (Required) Array of data objects to display (flat or hierarchical). | | columns | GridColumn[] | [] | (Required) Array of column definition objects. | | rowId | string | 'id' | Unique identifier property for each row. Crucial for all features. | | rowHeight | number | 40 | Row height in pixels. Required for virtual scrolling. | | verticalBorder | boolean | false | Toggles a vertical border between cells. | | stretch | boolean | false | Stretches the grid container to take its parent's full height. | | rowSelection | 'single' \| 'multiple' \| 'none' | 'none' | Defines row selection behavior. | | enableSorting | boolean | true | Enables sorting on columns marked as sortable. | | enableDragdrop | boolean | false | Enables column reordering by dragging and dropping the column headers. | | enableFilters | boolean | false | Enables built-in text filtering. (Future feature) | | pagination | boolean | false | Enables pagination. If false, grid uses virtual scrolling by default. | | recordsPerPage | number | 50 | Initial number of records per page (when pagination is enabled). | | recordSelectValues | number[] | [50, 100, 150] | Values for the "records per page" dropdown in the default footer. | | treeColumnKey | string | undefined | Key of the column to render the tree structure in (enables tree grid mode). | | expandAll | boolean | false | If true, all parent nodes in a tree grid will be expanded by default. | | loadTreeChildren | (row: GridRow) => Promise<GridRow[]> | undefined | Function to asynchronously load child nodes in a tree grid when a parent is expanded. | | autoScroll | ('sort' \| 'page' \| 'records')[] | [] | Array of actions that should trigger an automatic scroll to the top of the grid. | | isRowDisabled | (row: GridRow) => boolean | () => false | Function that returns true if a row should be disabled (cannot be selected). | | onApiReady | (api: ZunoPublicApi) => void | undefined | Callback invoked when the Grid API is ready, providing access to the API instance. |


🔹 Column Definitions (GridColumn)

Each column is defined as a GridColumn object in the columns array.

| Property | Type | Description | |----------|------|-------------| | key | string | (Required) Unique column identifier. Must match a property in the row data. | | header | string | (Required) Text to display in the column header. | | width | number | Initial column width in pixels. If omitted, width is evenly distributed among fluid columns. | | show | boolean | Whether the column is visible by default. Can be changed via the API. (default: true) | | minWidth | number | Minimum resizable width in pixels. (default: 30) | | maxWidth | number | Maximum resizable width in pixels. (default: Infinity) | | sortable | boolean | Enables sorting for this column. (default: false) | | resizable | boolean | Allows this column to be resized by the user. (default: true) | | draggable | boolean | Allows this column to be reordered via drag-and-drop. (default: false) | | quickActions | boolean | If true, shows the ellipsis (...) icon to open the column header menu. (default: false) | | pinned | 'left' \| 'right' | Pins the column to the left or right side of the grid. | | valueGetter | (params: GridValueParam) => any | Function to derive the display value for a cell. Useful for formatting or combining data. | | sortComparator | (a: GridRow, b: GridRow) => number | Custom sorting function for non-standard data types (e.g., dates, complex objects). | | sortTooltip | (nextDir: SortDirection \| null) => string | Function that returns custom tooltip text for the next sort action (asc, desc, or none). | | headerClass | string \| object \| any[] | CSS class(es) to apply to the header cell for this column. | | headerStyle | CSSProperties | Inline styles to apply to the header cell for this column. | | cellClass | string \| object \| any[] | CSS class(es) to apply to every body cell in this column. | | cellStyle | CSSProperties | Inline styles to apply to every body cell in this column. |


📢 Events

ZunoGrid uses events and callback props to communicate state changes and provide access to its internal API.

🔹 Emitted Events

You can listen for these standard Vue events on the <ZunoGrid /> component.

| Event | Payload | Description | |-------|---------|-------------| | @selection-changed | GridRow[] | Fires whenever the row selection is changed by the user. The payload is an array containing the full data objects of the selected rows. |


🔹 Callback Props

For more advanced interactions, the grid uses function props.
This pattern is used to provide a direct, synchronous reference to internal APIs.

| Prop | Signature | Description | |------|-----------|-------------| | onApiReady | (api: ZunoPublicApi) => void | Fires as soon as the Grid API is initialized, providing the API instance as its argument.(Also documented in the main Props API table, but included here for clarity on event-like behavior.) |


⚡️ Grid API (ZunoPublicApi)

For advanced control, ZunoGrid provides a powerful imperative API that allows you to programmatically read and manipulate the grid's state from outside the component.


1️⃣ Accessing the API

To get a reference to the API, use the @api-ready event prop.
It is recommended to store this in a ref so you can access it from anywhere in your component.

<template>
  <ZunoGrid :rows="rows" :columns="columns" @api-ready="onApiReady" />
  <button @click="hideEmailColumn">Hide Email Column</button>
</template>

<script setup lang="ts">
import { ref } from "vue";
import type { ZunoPublicApi } from "@sanjaym/zuno-grid-vue";

const gridApi = ref<ZunoPublicApi | null>(null);

function onApiReady(api: ZunoPublicApi) {
  // Store the API instance to use later
  gridApi.value = api;
}

function hideEmailColumn() {
  if (gridApi.value) {
    gridApi.value.columns.hide("email");
  }
}
</script>

2️⃣ API Reference

The API is organized into logical groups: columns, rows, and sorting.


📌 api.columns

Methods for controlling column visibility, pinning, and order.

| Method | Description | |--------|-------------| | state | A reactive ComputedRef of the current column structure. Access with .value. | | pin(key, direction) | Pins a column. direction can be 'left', 'right', or null to unpin. Preserves original order. | | hide(key \| key[]) | Hides one or more columns by their key(s). | | show(key \| key[]) | Shows one or more previously hidden columns. | | autosize(key \| key[]) | Resizes one or more columns to fit their content. | | setState(newState) | Applies a new column layout. Define the order and pinning for all columns. | | setColumnWidth(key, width) | Directly sets the width of a specific column in pixels. |

🔍 Reading Column State

const currentState = gridApi.value.columns.state.value;

// Example Output
// {
//   leftPinned: [ { key: "name", visible: true } ],
//   normal: [ { key: "email", visible: false }, { key: "status", visible: true } ],
//   rightPinned: []
// }

// Watch for changes
watch(() => gridApi.value?.columns.state.value, (newState) => {
  console.log("Column layout changed!", newState);
});

⚙️ Setting Column State

// Move 'status' to left-pinned and reorder normal columns
gridApi.value.columns.setState({
  leftPinned: ["status"],
  normal: ["name", "email", "department"],
});

📌 api.rows

Methods for manipulating and navigating row data.
All methods are tree-aware.

| Method | Description | |--------|-------------| | update(row) | Finds a row by ID anywhere in the tree and merges the new properties. Returns true. | | insert(row, opts) | Inserts a new row (or updates if ID exists). Use opts.parentId for tree. | | delete(rowId) | Finds and deletes a row by its ID anywhere in the tree. Returns true. | | scrollTo(val, opts) | Scrolls the grid to a specific row. Can search by property and scroll to a column. |


📑 scrollTo Options

| Option | Type | Description | |--------|------|-------------| | searchKey | string | Property to search against (default: rowId). | | columnKey | string | Column to scroll horizontally. | | block | 'start' \| 'center' \| 'end' | Vertical alignment of the row. | | inline | 'start' \| 'center' \| 'end' | Horizontal alignment of the cell. | | behavior | 'smooth' \| 'instant' | Scroll animation behavior. |


🧩 Example

// Update a nested child row
gridApi.value.rows.update({ emp_id: 39, name: "Hemang Bhatt (Updated)" });

// Insert a new child under an existing parent
gridApi.value.rows.insert({ emp_id: 41, name: "New Teammate" }, { parentId: 3949 });

// Scroll to a row by email and center it
gridApi.value.rows.scrollTo("[email protected]", {
  searchKey: "email",
  block: "center",
});

📌 api.sorting

Methods for programmatically controlling the grid's sort state.

| Method | Description | |--------|-------------| | state | A reactive ComputedRef of the current sort model (e.g., { name: 'asc' }). | | sort(key, direction) | Sorts a column. direction can be 'asc', 'desc', or null to clear. | | clear() | Clears all sorting from the grid. |


🧩 Example

// Get the current sort state
console.log(gridApi.value.sorting.state.value); // -> { status: "desc" }

// Sort by 'name' ascending
gridApi.value.sorting.sort("name", "asc");

// Clear all sorting
gridApi.value.sorting.clear();

🌳 Tree Grid Mode

ZunoGrid has first-class support for hierarchical data.
To enable this mode, you must provide the tree-column-key prop.

This tells the grid which column should contain the expand/collapse toggles and indentation.


1️⃣ Data Structure

Your rows data must be structured as a tree.
Each parent object should contain a children property containing its child rows.

Every object (parent or child) must have a unique identifier corresponding to row-id.

const hierarchicalRows = [
  {
    emp_id: 2355, // Unique ID
    name: "Arpit Shah",
    manager_id: null,
    children: [
      // The 'children' property
      {
        emp_id: 3949,
        name: "Ankit Agarwal",
        manager_id: 2355,
        children: [
          { emp_id: 39, name: "Hemang Bhatt", manager_id: 3949 },
          { emp_id: 40, name: "Jane Doe", manager_id: 3949 },
        ],
      },
    ],
  },
  {
    emp_id: 101,
    name: "Sarah Connor",
    manager_id: null,
    // No 'children' property → no expand toggle will be rendered.
  },
];
  • tree-column-key="name" → renders tree controls in the Name column
  • :expand-all="true" → expands all parent nodes on initial load

🧩 Advanced Customization with Slots

ZunoGrid provides slots for rendering flexibility.

Avoid using async import (defineAsyncComponent) to use any custom component inside the grid for better rendering. This will be applied to all n level component of that particular custom component.


🔹 Custom Cell Rendering (#[column.key])

You can provide your own Vue components inside cells using named slots.
The slot name must match the column key.

Example: Custom Status Badge

MyStatusBadge.vue

<template>
  <span class="badge" :class="statusClass">{{ value }}</span>
</template>

<script setup lang="ts">
import { computed } from "vue";
const props = defineProps<{ value: string }>();
const statusClass = computed(() => `status-${props.value.toLowerCase()}`);
</script>

<style scoped>
.badge {
  padding: 4px 8px;
  border-radius: 12px;
  font-weight: bold;
}
.status-active {
  background-color: #e6f4ea;
  color: #4caf50;
}
.status-inactive {
  background-color: #feeeee;
  color: #f44336;
}
</style>

MyGridComponent.vue

<template>
  <ZunoGrid :rows="rows" :columns="columns" row-id="id">
    <!-- Slot name #status matches column key -->
    <template #status="{ value, item }">
      <MyStatusBadge :value="value" />
    </template>
  </ZunoGrid>
</template>

The slot receives:

  • value → cell’s data
  • item → entire row object

🔹 Custom Pagination Footer (#zuno-footer-slot)

ZunoGrid’s footer is fully customizable via #zuno-footer-slot.
This slot provides the pagination API, letting you build a custom UI while reusing grid logic.

Example: Minimal Custom Footer

<template>
  <ZunoGrid :rows="rows" :columns="columns" pagination>
    <template
      #zuno-footer-slot="{ page, record, updatePage, updateRecordCounts }"
    >
      <div class="my-custom-footer">
        <span>Total Records: {{ record.total.value }}</span>

        <div class="my-custom-buttons">
          <button @click="updatePage(1)" :disabled="page.current.value === 1">
            First
          </button>
          <button
            @click="updatePage(page.current.value - 1)"
            :disabled="page.current.value === 1"
          >
            Prev
          </button>
          <span>Page {{ page.current.value }} of {{ page.total.value }}</span>
          <button
            @click="updatePage(page.current.value + 1)"
            :disabled="page.current.value === page.total.value"
          >
            Next
          </button>
          <button
            @click="updatePage(page.total.value)"
            :disabled="page.current.value === page.total.value"
          >
            Last
          </button>
        </div>

        <select @change="updateRecordCounts(Number($event.target.value))">
          <option>10</option>
          <option>25</option>
          <option>50</option>
        </select>
      </div>
    </template>
  </ZunoGrid>
</template>

🔹 Pagination API (ZunoPaginationApi)

The #zuno-footer-slot receives:

| Property | Type | Description | | -------------------- | -------------------------------------------------------------- | ----------------------------------------------------- | | recordsPerPage | Ref<number> | Current number of records per page. | | page | { current: Ref<number>, total: Ref<number> } | Current & total page numbers. | | record | { start: Ref<number>, end: Ref<number>, total: Ref<number> } | Record start, end, and total counts. | | updateRecordCounts | (count: number) => void | Updates number of records per page. | | updatePage | (page: number) => void | Navigate to a specific page number. | | pageNumbers | Ref<(number \| string)[]> | Array of page numbers for UI (e.g., [1, '...', 5]). |


🎨 Theming

ZunoGrid can be deeply customized to match your application’s design system.
The library exposes a simple and powerful API for setting and reacting to theme changes.


1️⃣ Applying a Theme

The recommended way to apply a theme is by using the setZunoTheme function in your application's entry file (e.g., main.ts).
This should be called before your Vue app is mounted.

  • setZunoTheme(theme) → Sets the global theme for all ZunoGrid components.
    • Accepts:
      • A plain JavaScript object
      • A Vue ref
      • A reactive object
    • If you pass a reactive object or ref, the grid’s theme will automatically update whenever the theme object changes.

📄 Example — Dynamic Theme (main.ts)

import { createApp, ref } from "vue";
import App from "./App.vue";
import ZunoGridPlugin, { setZunoTheme, type ZunoTheme } from "@sanjaym/zuno-grid-vue";
import "@sanjaym/zuno-grid-vue/dist/style.css";

// 1. Define your theme. It can be a ref or reactive() for dynamic changes.
const myTheme = ref<ZunoTheme>({
  primary: "#007ACC",
  borderRadius: "4px",
  headerBackground: "#2d3748",
  headerText: "#ffffff",
});

// 2. Apply the theme. Updates automatically when myTheme changes.
setZunoTheme(myTheme);

// Example: dynamically update theme later
// myTheme.value.primary = "#FF0000";

const app = createApp(App);

// 3. Register the plugin
app.use(ZunoGridPlugin);

app.mount("#app");

2️⃣ Reading Theme Values

If you are building custom components that need to stay consistent with the grid’s theme,
you can use the useZunoTheme composable.

  • useZunoTheme() → Can be called inside any Vue component’s setup().
    • Returns a reactive Ref<ZunoTheme> pointing to the current global theme.
    • Access values via .value when working inside setup.

📄 Example — Using Theme in a Custom Cell (MyCustomCell.vue)

<template>
  <div :style="{ color: theme.value.primary, fontFamily: theme.value.fontFamily }">
    Custom Cell
  </div>
</template>

<script setup lang="ts">
import { useZunoTheme } from "@sanjaym/zuno-grid-vue";

// `theme` is a Ref<ZunoTheme>
const theme = useZunoTheme();
</script>

3️⃣ Available Theme Properties (ZunoTheme)

The ZunoTheme interface defines all customizable properties.
For colors, you must provide standard hex codes (e.g., #RRGGBB). The grid will automatically convert them to the necessary rgb values for internal use.

| Property | Type | Default | Description | |------------------------|---------|----------------------------|---------------------------------------------------------------| | fontFamily | string | "-apple-system, ..." | Grid font family | | fontSize | string | "14px" | Base font size | | primaryText | string | rgb(33, 37, 41) | Primary text color | | secondaryText | string | rgb(108, 117, 125) | Secondary text color (placeholders, etc.) | | borderColor | string | rgb(226, 221, 239) | Border color | | borderRadius | string | "10px" | Border radius for the main wrapper and menus | | borderWidth | string | "0.3px" | Border width | | primary | string | rgb(123, 97, 255) | Primary brand color (icons, active elements) | | background | string | rgb(255, 255, 255) | Grid background | | negative | string | rgb(176, 0, 32) | Color for destructive actions (e.g., Remove) | | headerBackground | string | rgb(242, 240, 255) | Header background color | | headerText | string | rgb(92, 84, 112) | Header text color | | headerFontSize | string | "14px" | Header font size | | headerFontWeight | string | "600" | Header font weight | | headerHoverBackground | string | same as headerBackground | Header hover background | | resizerColor | string | same as primary | Column resize handle color | | cellPadding | string | "8px 12px" | Padding for header and body cells | | rowHoverBackground | string | rgb(248, 247, 255) | Row hover background | | selectedRowBackground | string | rgb(243, 242, 255) | Background for selected row | | pinShadowColor | string | rgb(228, 223, 255) | Pinned column shadow color | | pinShadowBlurRadius | string | "0px" | Pinned column shadow blur radius | | pinShadowOffset | number | 0 | Pinned column shadow horizontal offset (px) | | footerBackground | string | same as background | Pagination footer background | | tooltipFontSize | string | "12px" | Tooltip font size | | tooltipColor | string | rgb(249, 249, 249) | Tooltip text color | | tooltipBgColor | string | rgb(115, 115, 115) | Tooltip background color | | menuBgColor | string | same as background | Column menu background | | menuColor | string | same as primaryText | Column menu text color | | menuBorderRadius | string | same as borderRadius | Column menu border radius | | menuBoxShadow | string | "0px 0px 8px #7B61FF33" | Column menu box shadow | | menuMaxWidth | string | "256px" | Max width for the column menu dropdown | | menuMaxHeight | string | "300px" | Max height for the column menu dropdown | | iconColor | string | rgb(122, 122, 115) | Default icon color | | iconHoverColor | string | rgb(228, 223, 255) | Background color for icon containers on hover | | iconContainerRadius | string | "4px" | Border radius for icon containers | | treePadding | number | 24 | Indentation for each level in a tree grid (px) |


🛠️ Helpers (zunoHelper)

ZunoGrid exports a collection of powerful and pure utility functions for common tasks, especially color manipulation. These are the same functions the grid uses internally.
You can import them for use in your own components to ensure consistency.

import { zunoHelper } from "@sanjaym/zuno-grid-vue";

const rgbArray = zunoHelper.hexToRgb("#FF5733"); // -> [255, 87, 51]

| Function | Signature | Description | |----------|-----------|-------------| | hexToRgb | (hex: string, toString?: boolean) => [r,g,b] \| string | Converts a hex color string to an array of RGB numbers or a comma-separated string. | | hexToHsl | (hex: string) => {h, s, l} | Converts a hex color string to an HSL object. | | adjustHexOpacity | (baseHex: string, opacity: number, bgHex: string) => string | Accurately blends a hex color with a background color to simulate true opacity, returning a new hex code. | | hashForColor | (str: string) => number | Creates a simple, non-crypto hash from a string, useful for picking a consistent color from an array. | | generateColor | (colors: string[], val: string, opacity?: number) => string | Selects a color from the colors array based on the hash of val and optionally applies opacity. |