Vue Guide

Pagination

Build client-side paginated feeds with useConvexPaginatedQuery and Convex paginated query functions.

Use useConvexPaginatedQuery when you want a client-side feed that loads one page at a time and stays subscribed as more data arrives.

Write the Convex query

Your Convex query must accept paginationOpts and return a PaginationResult.

convex/tasks.ts
import { paginationOptsValidator } from 'convex/server'
import { v } from 'convex/values'
import { query } from './_generated/server'

export const paginated = query({
  args: {
    status: v.optional(v.string()),
    paginationOpts: paginationOptsValidator,
  },
  handler: async (ctx, { status, paginationOpts }) => {
    let q = ctx.db.query('tasks').order('desc')
    if (status)
      q = q.filter(doc => doc.eq(doc.field('status'), status))
    return await q.paginate(paginationOpts)
  },
})

You do not pass paginationOpts from the component. useConvexPaginatedQuery injects it for you.

Subscribe from the client

src/components/TaskFeed.vue
<script setup lang="ts">
import { useConvexPaginatedQuery } from 'vue-convex'
import { api } from '../convex/_generated/api'

const { data, isPending, isDone, isLoadingMore, loadMore } = useConvexPaginatedQuery(
  api.tasks.paginated,
  {},
  { numItems: 20 },
)
</script>

Reset on filter changes

Reactive arguments automatically clear the current pages and restart from the first page.

src/components/FilteredFeed.vue
<script setup lang="ts">
import { useConvexPaginatedQuery } from 'vue-convex'
import { api } from '../convex/_generated/api'

const status = ref('active')

const { data, loadMore, isDone } = useConvexPaginatedQuery(
  api.tasks.paginated,
  computed(() => ({ status: status.value })),
  { numItems: 20 },
)
</script>

Skip pagination until the inputs exist

const { data, isSkipped } = useConvexPaginatedQuery(
  api.tasks.paginated,
  computed(() => userId.value ? { userId: userId.value } : 'skip'),
  { numItems: 20 },
)

Use the optimistic helpers when needed

The package exports helpers such as insertAtTop, insertAtBottomIfLoaded, insertAtPosition, and optimisticallyUpdateValueInPaginatedQuery for use inside mutation optimistic updates.

Verify the result

The setup is working when:

  • the first page loads into data
  • loadMore() appends another page
  • changing the arguments resets the feed

useConvexPaginatedQuery is client-only. It does not run an SSR prefetch pass.

Next steps

Copyright © 2026