A flexible data table for displaying structured datasets with support for sorting, sticky headers, column filters (text, select, multiselect), row hover/stripe styles, and a responsive mobile cards view.

When to Use

  • For displaying structured datasets with named columns that benefit from sorting and filtering
  • In dashboards, admin panels, or reporting views where users need to scan, sort, and narrow down rows
  • When mobile responsiveness is required — the mobile cards view adapts the layout for small screens

When Not to Use

  • For simple key–value information — use a <dl> or a card layout instead
  • For lists without columns — use QContainedList
  • When the dataset is empty by design — show an QEmptyState rather than an empty table

Component Anatomy

  • Header row — column labels; sortable columns show sort direction arrows
  • Data rows — one row per item; hover adds a highlight on mouse-over, striped alternates background colors
  • Sticky header — header remains visible during scroll when stickyHeader is enabled
  • Column filters — optional filter row below the header; each column can have a text, select, or multiselect filter
  • Mobile cards — collapses columns into stacked card items on small viewports

Fields and Items model

The following Vue reference will be used for any use case below:

Container Background — Uniform Rows

All rows share the same --q-body-container-bg surface. Use this when the table sits inside a card or panel with a different background, so each row reads as a clean slot.


Container Background — Striped Rows

Alternates between --q-body-container-bg (odd) and --q-body-quaternary-color (even). The quaternary tone is subtle enough to preserve readability at any font size while still clearly separating rows at a glance.


Row Hover

Keep column headers visible while scrolling through long tables. Perfect for maintaining context when working with large datasets.

Props

PropTypeDefaultDescription
stickyHeaderbooleanfalseEnable sticky table header that remains visible during scroll
stickyHeaderMaxHeightstring"600px"Maximum height of the scrollable table area

Column Filters

Enable column-level filtering to help users quickly find the data they need. Supports text search, single select, and multi-select filters.

Field Configuration

Configure filtering for each column using the QTableField interface:

typescript
const fields: QTableField[] = [
  {
    key: "name",
    label: "Product Name",
    filterable: true,
    filterType: "text" // Text search with debounce
  },
  {
    key: "category",
    label: "Category",
    filterable: true,
    filterType: "select", // Single selection dropdown
    filterOptions: [
      { value: "Fruit", text: "Fruit" },
      { value: "Vegetable", text: "Vegetable" }
    ]
  },
  {
    key: "tags",
    label: "Tags",
    filterable: true,
    filterType: "multiselect", // Multiple selection with checkboxes
    filterOptions: [
      { value: "organic", text: "Organic" },
      { value: "local", text: "Local" },
      { value: "imported", text: "Imported" }
    ]
  }
];

Filter Types

  • text: Case-insensitive text search with 300ms debounce
  • select: Single selection dropdown with "All" option
  • multiselect: Multiple selection with checkboxes (AND logic)

Props

PropTypeDefaultDescription
columnFiltersbooleanfalseEnable column-level filtering
filtersQTableFilters{}v-model for current filter state

Mobile Cards View

Automatically switch to a card-based layout on mobile devices for better usability on smaller screens.

Field Configuration

Configure mobile display behavior using the QTableField interface:

typescript
const fields: QTableField[] = [
  {
    key: "name",
    label: "Product Name",
    mobileLabel: true // Display as card title
  },
  {
    key: "quantity",
    label: "Quantity",
    mobilePriority: 1 // Display order (lower = higher priority)
  },
  {
    key: "price",
    label: "Price",
    mobilePriority: 2
  },
  {
    key: "internal_id",
    label: "Internal ID",
    mobileHidden: true // Hide in mobile view
  }
];

Mobile Options

  • mobileLabel: Mark a field to display as the card title (defaults to first field)
  • mobilePriority: Control display order in cards (1 = highest priority)
  • mobileHidden: Hide field in mobile view
  • mobileSecondary: Mark field for secondary display styling

Props

PropTypeDefaultDescription
mobileCardsbooleanfalseEnable card view on mobile devices
mobileBreakpointBreakpointKey"isSmall"Breakpoint for switching to mobile view (< 768px)
mobileCardTitlestringundefinedField key to use as card title (overrides mobileLabel)

Combined Features

All features can be combined for a fully-featured responsive data table:

Props

PropTypeDefaultDescription
itemsTableItem[]requiredArray of data objects to display as table rows.
fieldsQTableField[]requiredColumn definitions (see QTableField type below).
hoverbooleanfalseHighlights rows on mouse-over.
stripedbooleanfalseAlternates background color on even/odd rows.
borderedbooleanfalseAdds borders to cells and the table outline.
smallbooleanfalseReduces cell padding for a more compact table.
stickyHeaderbooleanfalseKeeps the header row visible while scrolling.
stickyHeaderMaxHeightstring"600px"Maximum height of the scrollable area when stickyHeader is on.
columnFiltersbooleanfalseEnables per-column filter controls.
filtersQTableFilters{}Current filter state (use with v-model:filters).
mobileCardsbooleanfalseSwitches to a stacked card layout on small viewports.
mobileBreakpointBreakpointKey"isSmall"Viewport breakpoint below which the mobile cards view is applied.
mobileCardTitlestringField key to use as the card title, overriding mobileLabel.

QTableField Type

ts
type QTableField = {
  key: string;           // Property key in the item object
  label?: string;        // Column header text
  sortable?: boolean;    // Enables sort arrows on the column header
  formatter?: (value: any, key: string, item: any) => any; // Custom cell formatter
  filterable?: boolean;  // Enables a filter control for this column
  filterType?: "text" | "select" | "multiselect"; // Filter input type
  filterOptions?: { value: any; text: string }[]; // Options for select/multiselect filters
  mobileLabel?: boolean; // Use as card title in mobile cards view
  mobileSecondary?: boolean; // Secondary styling in mobile view
  mobilePriority?: number; // Display order in mobile cards (lower = higher priority)
  mobileHidden?: boolean; // Hide column in mobile cards view
};

Events

EventPayloadDescription
update:filtersQTableFiltersEmitted when a column filter value changes. Use with v-model:filters.
row-clickedTableItemEmitted when a data row is clicked, with the row's item data.
sort-changed{ key: string; order: "asc" | "desc" }Emitted when the user clicks a sortable column header.

Accessibility

  • Column headers use <th scope="col"> elements for proper screen reader association
  • Sortable columns communicate their state via aria-sort ("ascending" or "descending")
  • Row hover and striped styles are visual only; do not rely on them to convey row state
  • When using column filters, ensure filter inputs have visible labels or aria-label attributes
  • On mobile cards view, ensure the card layout maintains a logical reading order for screen readers