← All context files

vuetify3/data-display.md

Vuetify 3 – Data Display

v-data-table

The Vuetify 3 data table API has changed significantly from Vuetify 2.

Static data

<template>
  <v-data-table
    :headers="headers"
    :items="users"
    :search="search"
    :items-per-page="10"
    item-value="id"
  >
    <template #top>
      <v-text-field v-model="search" label="Search" clearable />
    </template>

    <template #item.status="{ item }">
      <v-chip :color="statusColor(item.status)">{{ item.status }}</v-chip>
    </template>

    <template #item.actions="{ item }">
      <v-btn icon="mdi-pencil" size="small" @click="edit(item)" />
      <v-btn icon="mdi-delete" size="small" color="error" @click="remove(item)" />
    </template>
  </v-data-table>
</template>

<script>
export default {
  data() {
    return {
      search: '',
      headers: [
        { title: 'Name',   key: 'name', sortable: true },
        { title: 'Email',  key: 'email' },
        { title: 'Status', key: 'status' },
        { title: '',       key: 'actions', sortable: false, width: 120 },
      ],
      users: [
        { id: 1, name: 'Ada',   email: 'ada@kyd.au',   status: 'active' },
        { id: 2, name: 'Grace', email: 'grace@kyd.au', status: 'inactive' },
      ],
    }
  },
  methods: {
    statusColor(s) { return s === 'active' ? 'success' : 'grey' },
    edit(item)   { /* ... */ },
    remove(item) { /* ... */ },
  },
}
</script>

Important Vuetify 3 changes

Server-side pagination → v-data-table-server

<v-data-table-server
  v-model:items-per-page="itemsPerPage"
  :headers="headers"
  :items="rows"
  :items-length="total"
  :loading="loading"
  @update:options="onOptions"
/>
methods: {
  async onOptions({ page, itemsPerPage, sortBy }) {
    this.loading = true
    try {
      const { rows, total } = await api.list({
        page, rowsPerPage: itemsPerPage,
        sortBy: sortBy[0]?.key,
        descending: sortBy[0]?.order === 'desc',
      })
      this.rows = rows
      this.total = total
    } finally {
      this.loading = false
    }
  },
}

v-list

<v-list density="compact" nav>
  <v-list-subheader>Filters</v-list-subheader>
  <v-list-item
    v-for="filter in filters"
    :key="filter.id"
    :title="filter.name"
    :subtitle="filter.count + ' items'"
    :prepend-icon="filter.icon"
    @click="apply(filter)"
  />
</v-list>

Selection model:

<v-list v-model:selected="selected" select-strategy="single">
  <v-list-item v-for="i in items" :key="i.id" :value="i.id" :title="i.name" />
</v-list>

v-card

<v-card variant="elevated">
  <v-card-title>User</v-card-title>
  <v-card-subtitle>{{ user.email }}</v-card-subtitle>
  <v-card-text>
    <p>Member since {{ user.joined }}</p>
  </v-card-text>
  <v-card-actions>
    <v-spacer />
    <v-btn variant="text" @click="cancel">Cancel</v-btn>
    <v-btn color="primary" @click="save">Save</v-btn>
  </v-card-actions>
</v-card>

variant options: elevated (default), flat, tonal, outlined, plain.

v-chip

<v-chip color="primary" size="small">tag</v-chip>
<v-chip closable @click:close="remove">tag</v-chip>

v-pagination

<v-pagination v-model="page" :length="totalPages" :total-visible="7" />

Pair with manual pagination of a list (v-data-table-server has its own built-in pagination, don't double up).