Composables
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.
| Option | Type | Default | Description |
|---|---|---|---|
lazy | boolean | false | Don't block navigation |
server | boolean | true | Fetch during SSR |
immediate | boolean | true | Fetch immediately |
watch | any[] | — | Refetch when values change |
transform | (data) => T | — | Transform response |
clientCache | boolean | true | Enable client-side caching |
getCachedData | function | — | Custom cache control function |
retry | boolean | number | false | Enable retry with optional count |
retryDelay | number | 1000 | Delay between retries (ms) |
timeout | number | 0 | Request 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
| Composable | Variables |
|---|---|
usePosts() | { first?: number } |
usePostByUri() | { uri: string } |
usePostById() | { id: string } |
usePostsByCategoryName() | { categoryName: string } |
usePostsByCategoryId() | { categoryId: number } |
Pages
| Composable | Variables |
|---|---|
usePages() | { first?: number } |
usePageByUri() | { uri: string } |
usePageById() | { id: string } |
Other
| Composable | Variables |
|---|---|
useMenu() | { name: string } |
useNodeByUri() | { uri: string } |
useGeneralSettings() | — |
useViewer() | — |
useRevisions() | { id: string } |
Navigation
| Composable | Description |
|---|---|
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:
| Field | Type | Description |
|---|---|---|
hasNextPage | boolean | Whether more items exist after this page |
hasPreviousPage | boolean | Whether items exist before this page |
endCursor | string | null | Cursor for fetching the next page |
startCursor | string | null | Cursor 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:
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>