Caching
Every composable call triggers a network request to your WordPress GraphQL API. Without caching, each page load costs ~200-500ms waiting for WordPress to respond. With caching enabled (the default), repeated requests resolve in ~1-5ms — making your site feel instant.
WPNuxt provides multiple layers of caching to achieve this. See How WPNuxt Works for where caching fits in the overall architecture.
Cache Layers
| Layer | Scope | Duration | Purpose |
|---|---|---|---|
| Server (Nitro) | All users | Configurable | Cache GraphQL responses server-side |
| Client (GraphQL) | Per browser | Session | Deduplicate identical queries during navigation |
| Payload | Per request | Hydration | Prevent refetch after SSR |
All layers work together automatically.
Server-Side Caching
WPNuxt caches GraphQL responses server-side using Nitro route rules.
How It Works
- First request fetches from WordPress (~200-500ms)
- Response is cached on the server
- Subsequent requests use cache (~1-5ms)
- With SWR enabled, stale content serves instantly while refreshing in background
Configuration
wpNuxt: {
cache: {
enabled: true, // Default: true
maxAge: 300, // Default: 300 (5 minutes)
swr: true // Default: true
}
}
| Option | Default | Description |
|---|---|---|
enabled | true | Enable/disable caching |
maxAge | 300 | Cache duration in seconds |
swr | true | Stale-while-revalidate |
Client-Side Caching
WPNuxt enables client-side GraphQL caching by default. This deduplicates identical queries during client-side navigation.
Per-Query Control
// Disable client caching for real-time data
const { data } = usePosts(undefined, { clientCache: false })
// Custom cache key for complex scenarios
const { data } = usePosts({ category: 'news' }, {
cacheKey: `posts-news-${locale.value}`
})
| Option | Type | Default | Description |
|---|---|---|---|
clientCache | boolean | true | Enable client-side caching |
cacheKey | string | - | Custom cache key suffix |
Payload Caching (getCachedData)
WPNuxt optimizes SSR payload handling with a smart getCachedData implementation:
- During hydration: Uses payload data (no refetch needed)
- Watch-triggered refetches: Uses cached data when available
- Manual refresh: Always fetches fresh data
Custom getCachedData
Override the default behavior for specific use cases:
const { data } = usePosts(undefined, {
getCachedData: (key, nuxtApp, ctx) => {
// Always fetch fresh
if (ctx.cause === 'refresh:manual') {
return undefined
}
// Use cached data
return nuxtApp.payload.data[key]
}
})
The ctx.cause can be:
'initial'- First load'refresh:manual'- Calledrefresh()'refresh:hook'- Triggered by Nuxt hook'watch'- Reactive dependency changed
Data Freshness Trade-offs
Caching makes your site fast, but it introduces a fundamental trade-off: cached content may be stale. Understanding this trade-off helps you choose the right caching strategy.
With SWR enabled (default)
When a visitor requests a page, they immediately receive the cached version — even if it's older than maxAge. In the background, the server fetches fresh data from WordPress and updates the cache. The next visitor sees the updated content.
This means content can be up to maxAge seconds old (5 minutes by default). For most content sites, this is an excellent trade-off: visitors never wait for WordPress, and content stays reasonably fresh.
Without SWR
When cache expires, the next visitor waits for a fresh WordPress response (~200-500ms) before seeing the page. Content is never older than maxAge, but some visitors experience slower page loads.
Preview mode
Preview mode automatically bypasses all caching — editors always see the latest draft content. See Preview Mode for setup details.
Choosing the right balance
For most WordPress content sites, the default configuration (maxAge: 300, swr: true) provides the best balance of speed and freshness. Content updates appear within 5 minutes, and visitors never wait for WordPress. If your site requires near-real-time updates, reduce maxAge or disable caching for specific queries. See Recommended Strategies below for common configurations.
When to Disable Caching
Development
// See changes immediately
wpNuxt: {
cache: {
enabled: false
}
}
Real-Time Content
// Per-query: disable for live data
const { data } = useLiveComments(undefined, { clientCache: false })
// Global: short cache with no SWR
wpNuxt: {
cache: {
maxAge: 60,
swr: false
}
}
Authenticated Content
Authenticated requests bypass server cache automatically (via Authorization header). For user-specific data, disable client caching. See also Preview Mode for how caching interacts with draft content previews.
const { data } = useViewer(undefined, { clientCache: false })
High-Traffic Sites
wpNuxt: {
cache: {
maxAge: 3600, // 1 hour
swr: true
}
}
Recommended Strategies
| Site Type | maxAge | swr | clientCache | Why |
|---|---|---|---|---|
| Blog / content site | 300–3600 | true | true | Content changes infrequently, SWR keeps it fresh |
| News / frequently updated | 60 | true | true | Short cache + SWR = fresh content without latency |
| Dashboard / real-time | 0 | false | false | Always fetch latest data |
| E-commerce catalog | 300 | true | true | Product data is mostly stable |
Cache Invalidation
Manual Refresh
const { data, refresh } = usePosts()
// Force fresh data
await refresh()
Reactive Invalidation
const category = ref('news')
const { data } = usePosts(
{ categoryName: category },
{ watch: [category] }
)
// Changing category triggers refetch
category.value = 'tech'
Server Cache
Server cache automatically expires based on maxAge. For immediate invalidation, you'll need to implement a webhook from WordPress or use a CDN with cache purge capabilities.
Related Pages
- Performance — Broader performance optimization strategies
- Preview Mode — How caching is bypassed for draft content
- Configuration — All cache configuration options