X Enterprises
Composables

useXAFContentQuery

Source-agnostic paginated content query state manager — plus typed wrappers useXAFReviews / useXAFArticles / useXAFCategories and a debounced useXAFSearch.

useXAFContentQuery

Generic content query state manager for the review-site family. Accepts a fetcher function and returns reactive pagination, loading, and error state. The composable is source-agnostic — the fetcher can call Nuxt Content's queryContent(), a custom API endpoint, or a static array filter.

useXAFContentQuery itself is auto-imported. The typed wrappers (useXAFReviews, useXAFArticles, useXAFCategories, useXAFSearch) are exported from the same file but are not auto-imported (multi-export file) — import them explicitly when you need them.

Usage

const { items, loading, error, page, hasMore, nextPage } = useXAFContentQuery(
  async ({ page, perPage }) => {
    const items = await queryContent('reviews')
      .skip((page - 1) * perPage)
      .limit(perPage)
      .find()
    return { items, total: 100 }
  },
  { perPage: 12 },
)

Parameters

ParamTypeDescription
fetcher(params: { page, perPage, sortBy?, sortOrder?, filter? }) => Promise<{ items: T[], total: number }>Async function returning the current page of items plus the total count.
options.pagenumberInitial page (1-indexed). Default 1.
options.perPagenumberItems per page. Default 12.
options.sortBy / options.sortOrderstring / 'asc' | 'desc'Sort hints passed through to the fetcher.
options.filterRecord<string, unknown>Arbitrary filter object passed through to the fetcher.

Returns

KeyTypeDescription
itemsRef<T[]>Fetched items for the current page.
loadingRef<boolean>Whether a fetch is in-flight.
errorRef<string | null>Error message, or null when idle/success.
totalRef<number>Total items across all pages (as reported by the fetcher).
page / perPageRef<number>Current page (1-indexed) and page size.
hasMoreComputedRef<boolean>true when there are more pages to fetch.
refresh()() => Promise<void>Re-fetch the current page.
nextPage() / prevPage()() => Promise<void>Advance / go back one page (no-op at the bounds).
setPage(p)(p: number) => Promise<void>Jump to an arbitrary page.

Typed wrappers

Same file, explicit import required:

import { useXAFReviews, useXAFSearch } from '@xenterprises/nuxt-x-affiliate/app/composables/useXAFContentQuery'

// Reviews: defaults sortBy "publishedAt" desc; options: { category, minRating, perPage, sortBy, sortOrder, filter }
const reviews = useXAFReviews(fetcher, { category: 'keyboards', minRating: 4 })

// Articles: options: { tag, author, perPage, sortBy, sortOrder, filter }
const articles = useXAFArticles(fetcher, { tag: 'testing' })

// Categories: same pagination shape, typed for XAFCategoryItem
const categories = useXAFCategories(fetcher)

// Search: debounced (300ms default), minChars 2, limit 24
const { hits, search, clear } = useXAFSearch(
  async (query) => ({ hits: await $fetch('/api/search', { query: { q: query } }), total: 42 }),
  { debounceMs: 300, minChars: 2, limit: 24 },
)

AI Context

composable: useXAFContentQuery
package: "@xenterprises/nuxt-x-affiliate"
use-when: >
  Paginated listing pages (/reviews, /articles, category indexes) and
  search. Source-agnostic fetcher pattern; returns reactive items /
  loading / error / page / hasMore state. Typed wrappers useXAFReviews,
  useXAFArticles, useXAFCategories, useXAFSearch live in the same file
  and need explicit imports (multi-export file, not in the auto-import
  preset).
Copyright © 2026