WPNuxt 2.0 shipped on March 14 with type-safe composables, multi-layer caching, Gutenberg block rendering, authentication, and an AI-powered MCP server. Three weeks and two minor releases later, several features that were on the 2.0 roadmap are now production-ready — along with a few that weren’t planned at all.
This post covers what’s new in WPNuxt 2.1 and 2.2. If you’re new to WPNuxt, start with the 2.0 announcement for the full picture.
Quick Recap: What WPNuxt 2.0 Brought
WPNuxt connects WordPress with Nuxt via GraphQL, generating fully typed composables from your queries. Version 2.0 introduced:
- Type-safe composables generated from GraphQL queries with full autocomplete
- Three-layer caching — server (Nitro SWR), client deduplication, and SSR payload
- Gutenberg block rendering with 10 built-in Vue components via
@wpnuxt/blocks - Authentication with password, OAuth, and external provider support via
@wpnuxt/auth - AI-powered development via MCP server integration
- Serverless-ready deployment — no jsdom, works on Vercel out of the box
- SSG support with prerender route fetching
- GraphQL mutations — generated
useMutation*()composables
All three packages (@wpnuxt/core, @wpnuxt/blocks, @wpnuxt/auth) are now at version 2.2.1.
Cursor-Based Pagination & Infinite Scroll
The most requested feature from the 2.0 roadmap. The new useWPConnection composable handles cursor-based pagination with built-in infinite scroll support:
const { data: posts, pageInfo, loadMore, pending } = await useWPConnection(
'Posts',
['posts'],
true,
() => ({ limit: 10 })
)data returns the accumulated nodes array. pageInfo provides hasNextPage, hasPreviousPage, startCursor, and endCursor. Calling loadMore() fetches the next page and appends the results — perfect for infinite scroll:
<template>
<div>
<article v-for="post in posts" :key="post.id">
<h2>{{ post.title }}</h2>
</article>
<button v-if="pageInfo?.hasNextPage" :disabled="pending" @click="loadMore()">
Load more
</button>
</div>
</template>Calling refresh() resets the accumulation and re-fetches from the beginning.
Reactive Parameters
Generated composables now accept reactive parameters via refs, computed values, or getter functions. When the parameters change, the composable automatically re-fetches:
const orderField = ref('DATE')
const order = ref('DESC')
const { data: posts } = await usePosts(() => ({
orderField: orderField.value,
order: order.value
}))Change orderField or order and the query re-executes. Watch-triggered re-fetches automatically skip the cache so you always get fresh data.
This works with all generated composables — usePosts, usePageByUri, usePostByUri, custom queries, and useWPConnection.
Orderby Variables in Default Queries
The default Posts, PostsByCategoryName, and PostsByCategoryId queries now accept order and orderField variables:
// Sort by title ascending
const { data: posts } = await usePosts({
orderField: 'TITLE',
order: 'ASC'
})
// Sort by comment count, most commented first
const { data: popular } = await usePostsByCategoryName({
categoryName: 'tutorials',
orderField: 'COMMENT_COUNT',
order: 'DESC'
})Available order fields: DATE, TITLE, MODIFIED, AUTHOR, COMMENT_COUNT. Defaults are DATE / DESC — matching WordPress behavior, so existing code is unaffected.
Type Guards for Content Types
Three new type guard helpers make content type narrowing clean and type-safe:
import { isPage, isPost, isContentType } from '#imports'
const { data: node } = await useNodeByUri({ uri: route.path })
if (isPage(node.value)) {
// TypeScript knows this is a Page — access page-specific fields
console.log(node.value.isFrontPage)
}
if (isPost(node.value)) {
// TypeScript knows this is a Post
console.log(node.value.categories)
}
// Works with custom post types too
if (isContentType(node.value, 'event')) {
console.log(node.value.contentTypeName) // 'event'
}All three are auto-imported and narrow the TypeScript type, so downstream code gets full autocomplete for the matched content type.
ACF Field Helpers
Working with Advanced Custom Fields through WPGraphQL often means dealing with quirky data shapes. Two new helpers smooth this out:
unwrapScalar
WPGraphQL types ACF select and radio fields as arrays, even for single-value selections. unwrapScalar normalizes this:
// ACF returns ['upcoming'] for a single-select field
const status = computed(() => unwrapScalar(event.value?.eventDetails?.eventStatus))
// → 'upcoming' (not ['upcoming'])unwrapConnection
WPGraphQL wraps ACF relationship and post object fields in a connection structure. unwrapConnection extracts the first node:
// ACF returns { nodes: [{ id: '1', title: 'Main Hall' }] } for a single relationship
const venue = computed(() => unwrapConnection(event.value?.eventDetails?.eventVenue))
// → { id: '1', title: 'Main Hall' }Both return undefined for null or empty values, making them safe to use in computed properties and templates.
Webhook Cache Revalidation
WPNuxt 2.0 already had multi-layer caching. Version 2.2 adds a webhook endpoint so WordPress can invalidate the cache immediately when content changes — no more waiting for the TTL to expire.
Enable it with a shared secret:
export default defineNuxtConfig({
wpNuxt: {
cache: {
enabled: true,
maxAge: 300,
swr: true,
revalidateSecret: process.env.WPNUXT_REVALIDATE_SECRET
}
}
})This registers a POST /api/_wpnuxt/revalidate endpoint. Configure a WordPress webhook (e.g. via WP Webhooks or a simple save_post action) to call it with the secret token when content is published or updated.
On Vercel, when VERCEL_TOKEN and VERCEL_PROJECT_ID environment variables are set, the endpoint also purges the Vercel CDN cache using cache tags — giving you near-instant content updates without a full redeploy.
On self-hosted deployments, it purges Nitro’s internal handler cache directly.
Deeper Inner Block Nesting
The @wpnuxt/blocks package now supports deeper nested inner blocks in Gutenberg content. Complex layouts with groups, columns, and nested containers render correctly without losing content at deeper levels. Block types have been consolidated for more consistent handling across nesting levels.
Upgrading
All features are backward-compatible. Update your packages to get everything:
pnpm update @wpnuxt/core @wpnuxt/blocks @wpnuxt/authNo configuration changes required — existing projects pick up the new composables and helpers through auto-imports.
What’s Next
With pagination, infinite scroll, and deeper block nesting shipped, the remaining roadmap items are:
- Additional default queries for taxonomies and search
- End-to-end tutorials for common use cases
- Media handling deep dive with
@nuxt/imageoptimization patterns
Feedback and feature requests are welcome on the GitHub repository.
Resources
← Previous
From Vibe Coding to Agentic Engineering
Vibe coding is fun until it breaks. Agentic engineering keeps the developer in charge. This is how I use it daily with Claude Code.
Next →
My Audio Runs on a Raspberry Pi — So I Built a Spectrum Visualizer That Doesn't Listen
FFT Visualizer 1.0 is out: a WebGL audio spectrum analyzer that draws spectra computed anywhere — born from a Pi control panel, grown into a TypeScript core with Vue and React wrappers
