A Nuxt module that seamlessly integrates WordPress with Nuxt via GraphQL (WPGraphQL), providing type-safe composables and utilities for fetching WordPress content.
- Release Notes
- Migration Guide (for users upgrading from 1.x)
This monorepo contains the following packages:
| Package | Description | Version |
|---|---|---|
| @wpnuxt/core | Core WordPress integration with GraphQL | |
| @wpnuxt/blocks | Gutenberg block rendering components | |
| @wpnuxt/auth | WordPress authentication (JWT) |
- Auto-generated Composables - Generates type-safe composables from your GraphQL queries
- Reactive Params - Pass
ref,computed, or getter functions as query variables with automatic re-fetching - Connection Queries - Cursor-based pagination with
pageInfo,loadMore()for infinite scroll, and page-based navigation - Type Safety - Full TypeScript support with generated types from your WordPress GraphQL schema
- Type Guards -
isPage(),isPost(),isContentType()for narrowing union types fromuseNodeByUri() - ACF Helpers -
unwrapScalar()andunwrapConnection()for normalizing ACF field return types - Query Merging - Extend or override default queries with your custom GraphQL queries
- Block Rendering - Render Gutenberg blocks as Vue components with
@wpnuxt/blocks - Image Optimization - NuxtImg integration for optimized WordPress images
- Sanitized Content - Integrated DOMPurify for safe HTML content rendering
- Vercel Ready - Optimized settings for Vercel deployment with ISR support
pnpm create wpnuxt@latestInstall the package:
pnpm add @wpnuxt/coreAdd to your Nuxt config:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@wpnuxt/core'],
wpNuxt: {
wordpressUrl: 'https://your-wordpress-site.com'
}
})<script setup lang="ts">
// Fetch posts
const { data: posts } = await usePosts()
// Fetch a single post by URI
const { data: post } = await usePostByUri({ uri: '/my-post' })
// Custom ordering (defaults: DATE / DESC)
const { data: sorted } = await usePosts({ orderField: 'TITLE', order: 'ASC' })
// Lazy loading (non-blocking) with reactive state
const { data: pages, pending, refresh } = usePages(undefined, { lazy: true })
</script>
<template>
<article v-for="post in posts" :key="post.id">
<h2>{{ post.title }}</h2>
<div v-sanitize-html="post.content" />
</article>
</template>Pass reactive variables to composables — they auto-refetch when values change:
const category = ref<string>()
const params = computed(() => ({
first: 20,
where: { categoryName: category.value }
}))
const { data: posts } = usePosts(params)
// Changes to category trigger automatic re-fetchQueries with pageInfo automatically get pagination support:
const { data, pageInfo, loadMore } = await useEvents({ first: 10 })
// data.value is EventFragment[]
await loadMore() // Fetches next page, appends to data// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@wpnuxt/core'],
wpNuxt: {
// Required: Your WordPress site URL (no trailing slash)
wordpressUrl: 'https://your-wordpress-site.com',
// Optional settings
graphqlEndpoint: '/graphql', // Default: '/graphql'
downloadSchema: true, // Default: true
debug: false, // Default: false
// Query folders
queries: {
extendFolder: 'extend/queries/', // Default
mergedOutputFolder: '.queries/' // Default
},
// Server-side caching
cache: {
enabled: true, // Default: true
maxAge: 300, // Default: 300 (5 minutes)
swr: true // Default: true (stale-while-revalidate)
}
}
})WPNUXT_WORDPRESS_URL=https://your-wordpress-site.com
WPNUXT_GRAPHQL_ENDPOINT=/graphql
WPNUXT_DOWNLOAD_SCHEMA=true
WPNUXT_DEBUG=falseCreate custom queries by adding .gql files to extend/queries/:
# extend/queries/CustomPosts.gql
query CustomPosts($categoryId: Int!) {
posts(first: 10, where: { categoryId: $categoryId }) {
nodes {
...Post
customField
}
}
}This generates a useCustomPosts() composable.
Add pageInfo to get pagination support automatically:
# extend/queries/PaginatedEvents.gql
query PaginatedEvents($first: Int = 10, $after: String) {
events(first: $first, after: $after) {
pageInfo { hasNextPage endCursor }
nodes { ...Event }
}
}This generates usePaginatedEvents() with data, pageInfo, and loadMore().
For rendering WordPress Gutenberg blocks as Vue components:
pnpm add @wpnuxt/blocks// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@wpnuxt/core', '@wpnuxt/blocks'],
wpNuxtBlocks: {
imageDomains: ['your-wordpress-site.com']
}
})<script setup lang="ts">
const { data: page } = await usePageByUri({ uri: route.path })
</script>
<template>
<BlockRenderer v-if="page" :node="page" />
</template>Override default block components by creating your own in components/blocks/:
components/
blocks/
CoreParagraph.vue # Override default paragraph rendering
CoreHeading.vue # Override default heading rendering
MyCustomBlock.vue # Custom block component
- Nuxt 4.0+ (for Nuxt 3, use WPNuxt 1.x)
- WordPress with WPGraphQL plugin installed
- Node.js 20+
# Install dependencies
pnpm install
# Prepare all packages
pnpm run dev:prepare
# Run playground (full features)
pnpm run dev
# Run core playground (core only)
pnpm run dev:core
# Run blocks playground
pnpm run dev:blocks
# Run tests
pnpm run test
# Type check
pnpm run typecheck
# Lint
pnpm run lintSee the Migration Guide for detailed instructions on upgrading from WPNuxt 1.x.
Key changes:
- Nuxt 4.0+ required (was Nuxt 3)
- Composables renamed:
useWPPosts→usePosts - Single composable pattern:
usePosts(undefined, { lazy: true })for non-blocking - Directive changed:
v-sanitize→v-sanitize-html