Skip to content

VqList

An infinite-scroll list component that fetches paginated data from an API and appends items as the user loads more. Integrates with VqTableFilter for filtering.

Props

PropTypeDefaultDescription
idstringrequiredUnique list ID — must match the paired VqTableFilter id
actionstringrequiredAPI endpoint for fetching items
pageSizenumber10Number of items to fetch per request

Default Slot Props

KeyTypeDescription
itemsTValue[]Array of all loaded items
loadMore() => voidManually trigger loading the next page
finishedbooleantrue when all items have been loaded
loadingbooleantrue while an API request is in progress

Features

  • Appends new items to the list on each load (true infinite scroll)
  • Resets to page 1 and reloads when filter values change
  • Provides vqList injection for VqListLoadMoreBtn

Basic Example

vue
<script setup>
import { VqList, VqListLoadMoreBtn } from "@qnx/vuetify";
</script>

<template>
  <VqList id="posts" action="posts/list" v-slot="{ items, loading }">
    <v-list>
      <v-list-item
        v-for="post in items"
        :key="post.id"
        :title="post.title"
        :subtitle="post.category"
      />
    </v-list>
    <v-skeleton-loader v-if="loading" type="list-item@3" />
    <VqListLoadMoreBtn />
  </VqList>
</template>

With Filters

vue
<script setup>
import { VqList, VqListLoadMoreBtn, VqTableFilter, VqTextField } from "@qnx/vuetify";
</script>

<template>
  <VqTableFilter id="products">
    <v-row>
      <v-col cols="6">
        <VqTextField name="search" label="Search" />
      </v-col>
      <v-col cols="6">
        <VqAutocomplete name="category" label="Category" :items="categories" />
      </v-col>
    </v-row>
  </VqTableFilter>

  <VqList id="products" action="products/list" :page-size="12" v-slot="{ items, loading, finished }">
    <v-row>
      <v-col v-for="product in items" :key="product.id" cols="12" md="4">
        <v-card>
          <v-card-title>{{ product.name }}</v-card-title>
          <v-card-text>{{ product.price }}</v-card-text>
        </v-card>
      </v-col>
    </v-row>
    <v-skeleton-loader v-if="loading" type="card@3" />
    <p v-if="finished && !items.length" class="text-center">No products found.</p>
    <VqListLoadMoreBtn />
  </VqList>
</template>

Type-safe with useVqList

vue
<script setup>
import { useVqList, VqListLoadMoreBtn } from "@qnx/vuetify";

interface Post {
  id: number;
  title: string;
  author: string;
}

const PostList = useVqList<Post>();
</script>

<template>
  <PostList id="posts" action="posts/list" v-slot="{ items }">
    <!-- items is typed as Post[] -->
    <v-list-item
      v-for="post in items"
      :key="post.id"
      :title="post.title"
      :subtitle="post.author"
    />
    <VqListLoadMoreBtn />
  </PostList>
</template>

Manual Load More (No Button)

Use the loadMore slot prop to trigger loading programmatically:

vue
<VqList id="posts" action="posts/list" v-slot="{ items, loadMore, finished, loading }">
  <v-list-item v-for="post in items" :key="post.id">{{ post.title }}</v-list-item>

  <v-btn
    v-if="!finished"
    :loading="loading"
    @click="loadMore"
    block
    class="mt-4"
  >
    Load More Posts
  </v-btn>

  <p v-if="finished" class="text-center text-grey mt-4">All posts loaded.</p>
</VqList>