Skip to content

<DfGrid> Component

The main grid component. Handles column layout, virtual scrolling, sorting, and filtering.

vue
<df-grid
  :columns="columns"
  :records="records"
  key-field="id"
/>

Props

PropTypeDefaultDescription
columnsResponsiveColumnDefinitionsColumn layout definitions. Can be a flat list or a responsive array. See Column Definitions.
recordsRowValue[]Array of row data objects. Each item should contain at least the keyField property.
keyFieldstringName of the property used as a unique row identifier (like a primary key).
activeColumnsstringName of the currently active responsive layout (matches cssClass of a ResponsiveColumnDefinition). Supports v-model.
sortStateSortStateExternal sort state. Use with v-model:sortState for controlled sorting. When omitted the grid sorts internally.
filterStateFilterStateExternal filter state. Use with v-model:filterState for controlled filtering. When omitted the grid filters internally.
showFilterRowbooleanfalseShow the filter input row below the column headers.
showStatusBarbooleanfalseShow the status bar below the filter row (displays active filter count).
showSummaryBarbooleanfalseShow the summary bar below the data rows. The bar also appears automatically when loading is true or when records is empty.
loadingbooleanfalseIndicates that data is being fetched. When true the default summary bar shows a loading spinner; the no-data indicator is suppressed even when records is empty.
mainShadowCountnumber500Number of rows rendered in the main shadow grid used for column width measurement. Rarely needs changing.
secondaryShadowCountnumber30Number of rows rendered in secondary shadow grids (one per responsive layout). Rarely needs changing.
rowClass(item: RowValue, index: number) => string | string[] | Record<string, boolean>zebra striping ('even'/'odd')Returns CSS classes applied to each data row card. Receives the row data object and its 0-based index. Return type matches Vue's :class binding — a string, an array, or an object. Overriding this prop replaces the default even/odd zebra striping entirely; include the logic yourself if you still want it.
selectionModeSelectionModenullActive selection mode (null, 'selection', or 'exclusion'). Use with v-model:selectionMode for controlled selection. See Selection.
selectionKeysSet<any>Set of selected (or excluded) row keys. Use with v-model:selectionKeys. See Selection.
excessiveScrollThresholdnumberPercentage (0–100) of the maximum overscroll displacement (60 px) at which the excessive-scroll event fires. When omitted the event is never emitted. See Pull to refresh.

Emits

EventPayloadDescription
clickGridClickEventFired on single click on a data row when selection mode is inactive. Not fired while selection is active (clicks toggle selection instead). Header clicks always fire.
dblclickGridClickEventFired on double click.
sortGridSortEventFired when the user interacts with a sortable column header.
filterGridFilterEventFired when any filter value changes.
load'vertical' | 'horizontal'Fired when the user scrolls within loadDistance px of the end of the list and the grid is not in loading state. Use this to fetch and append the next page. Set :loading="true" while fetching to suppress duplicate events. Proxied from the underlying virtual-scroll load event.
excessive-scrollnumberFired when the user overscrolls past the excessiveScrollThreshold. Payload is the signed displacement in pixels: positive = past the bottom, negative = past the top. Re-fires only after 1 second has elapsed and the overscroll has fallen back below the threshold. Requires excessiveScrollThreshold to be set.
update:activeColumnsstringFired when the grid's ResizeObserver selects a different responsive layout.
update:sortStateSortStateFired together with sort when sort state changes internally.
update:filterStateFilterStateFired together with filter when filter state changes internally.
update:selectionModeSelectionModeFired when selection mode changes. Use with v-model:selectionMode.
update:selectionKeysSet<any>, SelectionAction, key?Fired when the selected key set changes. action is 'add', 'remove', or 'clear'.

GridClickEvent

typescript
interface GridClickEvent {
  rowId: number | 'header';  // row index (0-based) or 'header'
  key: any;                  // value of keyField for this row, or 'header'
  rowData: RowValue | undefined;
  columnClasses: string[];   // CSS classes of the clicked cell (fieldName + custom cssClass, grid internals stripped)
  columnName?: string;       // fieldName of the clicked column
  event: MouseEvent | TouchEvent;
}

GridSortEvent

See Sorting → GridSortEvent.

GridFilterEvent

See Filtering → GridFilterEvent.

Slots

SlotScopeDescription
toolbar-startRendered at the left side of the toolbar bar above the header. The toolbar wrapper (div.df-grid-toolbar) is only rendered when at least one toolbar slot is provided.
toolbar-endRendered at the right side of the toolbar.
header{ column, sortState, filterState, ... }Overrides the default header cell rendering. Passed through to <DfGridHeader>.
statusBar{ filterState }Overrides the default status bar content (filter count). Only visible when showStatusBar is true and selection mode is inactive.
groupActionsRendered on the right side of the selection status bar. Only visible while selection mode is active. Use it for batch actions (delete, export, …).
item{ item, index, active }Overrides the default <GridCard> row rendering. Receives the raw row object, its index, and the virtual scroller's active flag.
summary-barReplaces the entire summary bar content. The summary bar is visible when showSummaryBar is true, loading is true, or records is empty.
loadingReplaces the default loading indicator (spinning icon + "Loading…" text) inside the summary bar. Only rendered when loading is true.
no-dataReplaces the default no-data indicator (database-off icon + "No data" text) inside the summary bar. Only rendered when loading is false and records is empty.
footer-startRendered at the left side of the footer bar below the scroller. The footer wrapper (div.df-grid-footer) is only rendered when at least one footer slot is provided.
footer-endRendered at the right side of the footer.

Grid structure

The grid renders the following layers from top to bottom. Each section carries a data-section attribute so you can target it from CSS or event handlers without relying on internal class names.

┌─────────────────────────────────────┐  data-section="toolbar"
│  toolbar-start    │    toolbar-end  │  div.df-grid-toolbar  (only when a slot has content)
├─────────────────────────────────────┤  data-section="header"
│           column headers            │  df-grid-header component root
├─────────────────────────────────────┤  data-section="filter"   (inside header, when showFilterRow)
│           filter row                │
├─────────────────────────────────────┤  data-section="status-bar" (inside header, when visible)
│           status bar                │
├─────────────────────────────────────┤  data-section="body"
│           data rows                 │  virtual-scroll container
├─────────────────────────────────────┤  data-section="summary-bar" (when visible)
│           summary bar               │  div.df-summary-bar
├─────────────────────────────────────┤  data-section="footer"
│  footer-start     │    footer-end   │  div.df-grid-footer   (only when a slot has content)
└─────────────────────────────────────┘

data-section values:

ValueElementNotes
toolbardiv.df-grid-toolbarPresent only when toolbar-start or toolbar-end slot has content
headerdf-grid-header rootAlways present
filterFilter row div inside headerPresent only when showFilterRow is true
status-barStatus bar div inside headerPresent when showStatusBar is true or selection mode is active
bodyVirtual scroll containerAlways present
summary-bardiv.df-summary-barPresent when showSummaryBar is true, loading is true, or records is empty
footerdiv.df-grid-footerPresent only when footer-start or footer-end slot has content

The grid's own mouse event handler (processMouse) reads data-section first and immediately ignores clicks that originate in toolbar, filter, status-bar, and footer — those sections never trigger row interactions or selection.

Both div.df-grid-toolbar and div.df-grid-footer use display: flex; justify-content: space-between and are only mounted when at least one of their two slots has content.

Row CSS classes

The rowClass prop lets you attach per-row CSS classes based on row data or position. It receives the raw row object and its 0-based index, and its return value is passed directly to Vue's :class binding.

vue
<!-- Highlight negative amounts in red, keep zebra striping for the rest -->
<df-grid
  :columns="columns"
  :records="records"
  key-field="id"
  :row-class="(item, index) => item.amount < 0 ? 'negative' : (index % 2 === 0 ? 'even' : 'odd')"
/>
vue
<!-- Data-driven classes stored in the record itself -->
<df-grid
  :columns="columns"
  :records="records"
  key-field="id"
  :row-class="(item) => item.cssClass"
/>

When rowClass is omitted the default is (item, index) => index % 2 === 0 ? 'even' : 'odd', which produces the standard zebra striping. Providing your own function replaces this default entirely.

Example

vue
<df-grid :columns="columns" :records="records" key-field="id">
  <template #toolbar-start>
    <h2>My Grid</h2>
  </template>
  <template #toolbar-end>
    <button @click="addRow">Add row</button>
  </template>
  <template #footer-start>
    <span>{{ records.length }} records</span>
  </template>
  <template #footer-end>
    <span>Page 1 of 10</span>
  </template>
</df-grid>

Pull to refresh

The grid detects overscroll gestures (scrolling past the top or bottom edge) and exposes them through the excessive-scroll event. The consumer is responsible for acting on the event — the grid only detects the gesture and emits the notification.

Set excessiveScrollThreshold to the percentage of the maximum overscroll displacement (60 px) that should trigger the event. A value of 80 fires when the user has scrolled 48 px past the edge. The visual overscroll indicator (a blue gradient) is always rendered regardless of whether the event is listened to.

Firing conditions

The event fires immediately when the threshold is first crossed. After firing it is silenced until both of the following are true:

  1. At least 1 second has elapsed since the last firing.
  2. The overscroll displacement has fallen back below the threshold (the user released the gesture or stopped scrolling).

This prevents repeated rapid firings while the user holds the scroll position above the threshold.

Payload

ValueMeaning
positive (amount > 0)Overscroll past the bottom of the list (pull-to-load-more)
negative (amount < 0)Overscroll past the top of the list (pull-to-refresh)

Example — pull-to-refresh at the top

vue
<template>
  <df-grid
    :columns="columns"
    :records="records"
    key-field="id"
    :loading="loading"
    :excessive-scroll-threshold="80"
    @excessive-scroll="onExcessiveScroll"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue';

const loading = ref(false);
const records = ref([]);

async function onExcessiveScroll(amount: number) {
  if (amount < 0 && !loading.value) {
    // Negative amount = user pulled past the top → refresh
    loading.value = true;
    records.value = await fetchRecords();
    loading.value = false;
  }
}
</script>

Released under the MIT License.