How WPNuxt Works
WPNuxt connects three layers: WordPress stores your content, GraphQL exposes it as structured data, and Nuxt renders it as a fast, modern website. This page explains how the pieces fit together.
The Mental Model
WordPress (CMS) → WPGraphQL (API) → WPNuxt (bridge) → Nuxt (frontend)
- WordPress is your content management system — editors create posts, pages, and menus here
- WPGraphQL is a WordPress plugin that exposes content through a GraphQL API
- WPNuxt is a Nuxt module that generates type-safe composables from GraphQL queries
- Nuxt renders the content as server-rendered (or static) pages with Vue components
Why This Architecture?
Each layer in the stack exists for a specific reason:
Why GraphQL over REST? WordPress has a built-in REST API, but it returns fixed data shapes — every post response includes all fields whether you need them or not. GraphQL lets WPNuxt request exactly the fields each page needs, resulting in smaller payloads and fewer round trips. You have full control over which data to fetch by writing your own queries or extending the defaults. More importantly, the GraphQL schema is machine-readable, which enables WPNuxt to generate TypeScript types automatically.
Why build-time code generation?
WPNuxt generates composables and their TypeScript types at build time (during nuxt prepare), not at runtime. This means your editor knows the exact shape of usePosts() return data while you're writing code — you get autocomplete, type checking, and compile-time error detection. The trade-off is that you need to run pnpm nuxt prepare after changing .gql files to regenerate types.
Why server-side caching by default?
WordPress typically responds in 200-500ms per GraphQL query. For a page that makes multiple queries, this adds up quickly. Server-side caching with SWR (stale-while-revalidate) means visitors always get a near-instant response (~1-5ms) from the cache, while fresh data is fetched in the background. The trade-off is that content may be up to maxAge seconds stale (5 minutes by default). See Data Freshness Trade-offs for how to tune this balance.
Request Flow
When a visitor opens a page on your site, here's what happens:
Browser
→ Nuxt route (e.g. /blog/my-post)
→ composable (e.g. useNodeByUri)
→ GraphQL middleware (Nitro server)
→ Server cache check
→ HIT: return cached response (~1-5ms)
→ MISS: fetch from WordPress GraphQL API (~200-500ms)
→ cache response, return to browser
On the first request, WPNuxt fetches data from WordPress via GraphQL. On subsequent requests, the server cache returns the response instantly. With SWR (stale-while-revalidate) enabled, visitors always get a fast response while fresh data is fetched in the background.
The Three Packages
| Package | What It Does | When You Need It |
|---|---|---|
@wpnuxt/core | GraphQL integration, auto-generated composables, caching | Always — this is the foundation |
@wpnuxt/blocks | Renders Gutenberg blocks as Vue components | When you want per-block control (custom styling, lazy images, interactive blocks) |
@wpnuxt/auth | WordPress authentication (password, OAuth) | When editors need to log in through your Nuxt frontend (e.g. preview mode, gated content) |
Most projects start with @wpnuxt/core alone. Add @wpnuxt/blocks when you need fine-grained control over how WordPress blocks render. Add @wpnuxt/auth when you need authentication.
How Composables Are Generated
WPNuxt's key feature is auto-generated composables. Here's the build-time pipeline that makes this work:
1. GraphQL files (.gql)
├── Default queries (src/runtime/queries/) ← ships with WPNuxt
└── Your custom queries (extend/queries/) ← you create these
2. Query merging
└── Both sets are copied to .queries/ folder
(your files override defaults with the same name)
3. Parsing
└── Each query is parsed to extract name, variables, and return type
4. Code generation
└── For each query "Foo", WPNuxt generates:
├── useFoo() — blocks navigation until data is ready (good for SEO)
└── useAsyncFoo() — loads in the background (good for secondary content)
5. Output
├── .nuxt/wpnuxt/index.mjs — composable implementations
└── .nuxt/wpnuxt/index.d.ts — TypeScript declarations
This is why pnpm nuxt prepare is needed after adding or changing .gql files — it triggers the pipeline that generates your composables and their types.
usePosts() is available in any component without an import statement.Caching Layers
WPNuxt uses three layers of caching, all enabled by default:
Layer 1: Client cache (per browser)
└── Deduplicates identical GraphQL queries during navigation
(e.g. navigating back to a page you already visited)
Layer 2: Server cache (Nitro, shared across all users)
└── Caches GraphQL responses server-side
First request: ~200-500ms (fetches from WordPress)
Cached request: ~1-5ms
Layer 3: Payload cache (per request)
└── Prevents refetch during SSR → client hydration
(the data fetched on the server is reused on the client)
For most sites, the defaults work well. See Caching for how to tune cache duration, disable caching for specific queries, or handle cache invalidation.
Where to Go Next
- Quick Start — Build a blog in 15 minutes
- Fetching Data — Learn about composable options (lazy loading, watching, refreshing)
- Custom Queries — Fetch exactly the data you need
- Configuration — All configuration options