Reference

Composables

Composables API reference

Return Value

All composables return:

const {
  data,           // ComputedRef<T | undefined> - The fetched data
  pending,        // Ref<boolean> - Loading state
  error,          // Ref<Error | undefined> - Error if failed
  status,         // Ref<'idle' | 'pending' | 'success' | 'error'>
  refresh,        // () => Promise<void> - Refetch data
  execute,        // () => Promise<void> - Execute query
  clear,          // () => void - Clear data and error
  transformError, // Ref<Error | undefined> - Error during data transformation
  retryCount,     // Ref<number> - Number of retries attempted
  isRetrying,     // Ref<boolean> - Whether currently retrying
} = usePosts()

Options

usePosts(variables?, options?)

Variables can be a plain object, a ref, a computed, or a getter function. When reactive, the composable auto-refetches when the value changes.

OptionTypeDefaultDescription
lazybooleanfalseDon't block navigation
serverbooleantrueFetch during SSR
immediatebooleantrueFetch immediately
watchany[]Refetch when values change
transform(data) => TTransform response
clientCachebooleantrueEnable client-side caching
getCachedDatafunctionCustom cache control function
retryboolean | numberfalseEnable retry with optional count
retryDelaynumber1000Delay between retries (ms)
timeoutnumber0Request timeout (ms), 0 = disabled

Examples

// Client-only
usePosts(undefined, { server: false })

// Manual execution
const { execute } = usePosts(undefined, { immediate: false })
await execute()

// Reactive params (auto-refetches when params change)
const filters = reactive({ category: undefined as string | undefined })
const params = computed(() => ({
  first: 20,
  where: { categoryName: filters.category }
}))
usePosts(params)

// Watch reactive value (alternative to reactive params)
const category = ref('news')
usePosts({ categoryName: category }, { watch: [category] })

// Transform
usePosts(undefined, {
  transform: (posts) => posts?.map(p => p.title)
})

// Disable client caching for real-time data
usePosts(undefined, { clientCache: false })

// Enable retry on failure (3 attempts)
usePosts(undefined, { retry: 3 })

// Custom retry delay
usePosts(undefined, { retry: 3, retryDelay: 2000 })

// Enable timeout (10 seconds)
usePosts(undefined, { timeout: 10000 })

Default Composables

Posts

ComposableVariables
usePosts(){ first?: number }
usePostByUri(){ uri: string }
usePostById(){ id: string }
usePostsByCategoryName(){ categoryName: string }
usePostsByCategoryId(){ categoryId: number }

Pages

ComposableVariables
usePages(){ first?: number }
usePageByUri(){ uri: string }
usePageById(){ id: string }

Other

ComposableVariables
useMenu(){ name: string }
useNodeByUri(){ uri: string }
useGeneralSettings()
useViewer()
useRevisions(){ id: string }
ComposableDescription
usePrevNextPost(slug)Get previous/next posts for navigation
const { prev, next } = await usePrevNextPost('my-post-slug')
// prev/next contain: { slug, uri, title }

Lazy Mode

Use the lazy: true option for non-blocking navigation:

// Navigation happens immediately, data loads in background
usePosts({}, { lazy: true })
usePostByUri({ uri }, { lazy: true })
usePages({}, { lazy: true })

Connection Queries (Pagination)

When a query includes pageInfo alongside nodes, the generated composable automatically supports pagination. It returns data (the nodes array), pageInfo, and loadMore():

const {
  data,           // ComputedRef<T[] | undefined> - The items
  pageInfo,       // ComputedRef<WPPageInfo | undefined> - Cursor info
  loadMore,       // () => Promise<void> - Fetch next page and append
  pending,        // Ref<boolean>
  refresh,        // () => Promise<void> - Reset and refetch from scratch
  error,          // Ref<Error | undefined>
  status,         // Ref<'idle' | 'pending' | 'success' | 'error'>
} = await usePaginatedPosts({ first: 10 })

WPPageInfo contains:

FieldTypeDescription
hasNextPagebooleanWhether more items exist after this page
hasPreviousPagebooleanWhether items exist before this page
endCursorstring | nullCursor for fetching the next page
startCursorstring | nullCursor for fetching the previous page

Infinite Scroll (loadMore)

Use loadMore() to fetch the next page and append items to the existing list:

const { data: posts, pageInfo, loadMore, pending } = await usePaginatedPosts({ first: 10 })
// posts.value has first 10 items

await loadMore()
// posts.value now has 20 items (first + second page)

await loadMore()
// posts.value now has 30 items

refresh() resets the accumulation and re-fetches from the first page.

Page-based Navigation

For previous/next page navigation, manage the cursor yourself with reactive params:

const after = ref<string>()
const params = computed(() => ({ first: 10, after: after.value }))
const { data: posts, pageInfo } = await usePaginatedPosts(params)

// Navigate to next page
after.value = pageInfo.value?.endCursor

See Pagination for complete examples of both patterns.

Custom Composables

Create .gql files in extend/queries/ to generate custom composables:

extend/queries/FeaturedPosts.gql
query FeaturedPosts($limit: Int = 5) {
  posts(first: $limit, where: { tag: "featured" }) {
    nodes { ...Post }
  }
}

Generates useFeaturedPosts({ limit }) which you can use with the { lazy: true } option when needed.

Utilities

getRelativeImagePath

Converts WordPress absolute image URLs to relative paths for use with NuxtImage:

const imageUrl = 'https://wordpress.example.com/wp-content/uploads/2024/01/photo.jpg'
const relativePath = getRelativeImagePath(imageUrl)
// Returns: '/wp-content/uploads/2024/01/photo.jpg'

Usage with NuxtImage:

<NuxtImg :src="getRelativeImagePath(post.featuredImage?.node?.sourceUrl)" />

Content Type Guards

Type guards for narrowing the union type returned by useNodeByUri():

const { data } = await useNodeByUri({ uri: '/about' })

if (isPage(data.value)) {
  // data.value narrowed — access page-specific fields
  console.log(data.value.isFrontPage)
}

if (isPost(data.value)) {
  // data.value narrowed — access post-specific fields
  console.log(data.value.categories)
}

// For custom post types
if (isContentType(data.value, 'event')) {
  // data.value.contentTypeName narrowed to 'event'
}

unwrapScalar

Normalizes ACF select/radio fields that WPGraphQL returns as arrays regardless of single/multi configuration:

const status = unwrapScalar(event.value?.eventDetails?.eventStatus)
// string[] → first element
// string → passthrough
// null/undefined → undefined

unwrapConnection

Extracts the first node from a WPGraphQL connection, useful for ACF relationship fields configured as single-value:

const venue = unwrapConnection(event.value?.eventDetails?.eventVenue)
// { nodes: [{ title: 'Main Hall' }] } → { title: 'Main Hall' }
// { nodes: [] } → undefined
// null/undefined → undefined

TypeScript

Types are auto-generated from your GraphQL schema:

import type { PostFragment } from '#graphql-operations'

const { data: posts } = usePosts()
// posts: ComputedRef<PostFragment[] | undefined>
Copyright © 2026