Skip to main content

Reactive Arguments

Re-run a Convex query when filters, route parameters, or selected records change.

Query arguments can be a plain object, a ref, a computed value, or a getter. Nested refs are unwrapped before execution.

Filter a list

ts
const search = ref('')
const includeArchived = ref(false)

const args = computed(() => ({
  search: search.value.trim(),
  includeArchived: includeArchived.value,
}))

const projects = await useConvexQuery(api.projects.search, args, {
  keepPreviousData: true,
})

When either input changes, the composable requests the new argument set.

Keep the previous result deliberately

With keepPreviousData: true:

  1. The previous successful data remains visible.
  2. pending becomes true for the new arguments.
  3. isStale becomes true.
  4. The new successful result replaces the previous value.
vue
<template>
  <ProjectList :projects="projects.data.value ?? []" :dimmed="projects.isStale.value" />
</template>

The default is false, which clears the old result for a more explicit loading transition.

Skip incomplete arguments

Do not send placeholder IDs:

ts
const args = computed(() => {
  const id = route.params.projectId
  return typeof id === 'string' ? { projectId: id } : 'skip'
})

const project = await useConvexQuery(api.projects.get, args)

Use only the literal 'skip'. null and undefined are not public skip sentinels.

Auth-dependent execution

Prefer auth: 'required' over manually reading auth state only to skip a private query:

ts
const projects = await useConvexQuery(
  api.projects.mine,
  {},
  {
    auth: 'required',
  },
)

Use a computed 'skip' when another application condition also determines whether the query is meaningful.

Avoid side effects in argument getters

Vue may evaluate a computed value more than once. Argument getters should only derive data. Do not navigate, mutate state, or perform network work inside them.