
WPNuxt è un modulo per Nuxt pensato per integrare WordPress in modalità headless tramite GraphQL.
In pratica, permette di usare WordPress come CMS per la gestione dei contenuti e Nuxt come frontend moderno, veloce, tipizzato e ottimizzato per la SEO.
Rispetto a un’integrazione headless tradizionale basata sulle REST API, WPNuxt introduce un approccio più strutturato: usa WPGraphQL, genera composable type-safe e consente di recuperare contenuti WordPress con funzioni semplici come usePosts(), usePages() o useMenu().
Se stai partendo da zero, ti consiglio prima di leggere anche la guida su <a href=”/wordpress-headless-nuxt/”>WordPress headless con Nuxt</a>, dove viene spiegata la configurazione base con fetch nativo e REST API.
Cos’è WPNuxt
WPNuxt è un layer di integrazione tra WordPress, WPGraphQL e Nuxt.
L’architettura può essere riassunta così:
WordPress → WPGraphQL → WPNuxt → Nuxt
WordPress resta il backend editoriale.
WPGraphQL espone i contenuti tramite API GraphQL.
WPNuxt genera composable tipizzati a partire dalle query GraphQL.
Nuxt renderizza il frontend con Vue, SSR, SSG o caching lato server.
Secondo la documentazione ufficiale, WPNuxt collega WordPress a Nuxt 4 tramite GraphQL e fornisce composable type-safe per recuperare contenuti WordPress.
Perché usare WPNuxt in un progetto headless WordPress
WPNuxt è utile quando vuoi mantenere WordPress come CMS, ma non vuoi usare il tema WordPress tradizionale per il frontend.
I vantaggi principali sono:
- frontend più veloce e moderno con Nuxt
- migliore controllo su SEO, performance e Core Web Vitals
- query GraphQL più precise rispetto alle REST API
- TypeScript e autocomplete sui dati WordPress
- composable generati automaticamente
- supporto a Gutenberg tramite componenti Vue
- caching lato server con stale-while-revalidate
- possibilità di usare SSG o SSR in base al progetto
La differenza principale rispetto a un approccio manuale è che non devi scrivere ogni volta fetch, query, tipi TypeScript e logica di gestione dello stato. WPNuxt automatizza buona parte di questo lavoro.
Quando scegliere WPNuxt
WPNuxt è particolarmente indicato per:
- blog headless
- magazine online
- siti corporate
- landing page gestite da WordPress
- portali editoriali
- siti multilingua
- progetti con custom post type
- siti dove serve controllo completo sul frontend
- progetti in cui vuoi mantenere Gutenberg come editor
Non è invece sempre la scelta migliore per piccoli siti vetrina statici, dove una semplice integrazione via REST API può essere più che sufficiente.
Requisiti per usare WPNuxt
Per usare WPNuxt servono:
- un progetto Nuxt 4
- Node.js 20 o superiore
- un sito WordPress attivo
- il plugin WPGraphQL installato su WordPress
- conoscenze base di Vue, Nuxt e TypeScript
La documentazione ufficiale indica Nuxt 4.0+, Node.js 20+ e WordPress con plugin WPGraphQL come requisiti per l’installazione manuale.
Installazione di WPNuxt
Puoi creare un nuovo progetto WPNuxt con il comando:
pnpm create wpnuxt@latest
In alternativa, puoi aggiungere WPNuxt a un progetto Nuxt già esistente:
pnpm add @wpnuxt/core
Poi configura il modulo in nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@wpnuxt/core'],
wpNuxt: {
wordpressUrl: 'https://example.com'
}
})
Puoi anche usare una variabile d’ambiente:
WPNUXT_WORDPRESS_URL=https://example.com
Questa soluzione è preferibile quando lavori con più ambienti, ad esempio sviluppo, staging e produzione.
Configurare WordPress con WPGraphQL
WPNuxt non usa le REST API native di WordPress, ma GraphQL.
Per questo è necessario installare il plugin WPGraphQL.
Dopo l’installazione, l’endpoint GraphQL sarà generalmente disponibile a questo indirizzo:
https://example.com/graphql
Per verificare che tutto funzioni, puoi aprire l’endpoint nel browser ed eseguire una query come questa:
{
posts {
nodes {
title
uri
}
}
}
Se WordPress restituisce l’elenco degli articoli, il backend è pronto per essere usato da Nuxt.
WPGraphQL è richiesto perché WordPress non espone GraphQL di default; il plugin aggiunge l’API GraphQL che WPNuxt utilizza per leggere contenuti e generare tipi.
Recuperare gli articoli con WPNuxt
Uno dei vantaggi principali di WPNuxt è la semplicità dei composable.
Con una configurazione manuale dovresti scrivere una chiamata API, gestire il caricamento, gli errori e i tipi.
Con WPNuxt puoi fare così:
<script setup lang="ts">
const { data: posts, pending, error } = await usePosts()
</script>
<template>
<main>
<h1>Ultimi articoli</h1>
<p v-if="pending">Caricamento articoli...</p>
<p v-else-if="error">Errore nel caricamento degli articoli.</p>
<article v-for="post in posts" :key="post.id">
<NuxtLink :to="post.uri">
<h2>{{ post.title }}</h2>
</NuxtLink>
</article>
</main>
</template>
Il composable usePosts() recupera gli articoli WordPress e restituisce dati già utilizzabili nel componente.
WPNuxt include composable come usePosts(), usePostByUri(), usePages(), usePageByUri(), useMenu() e useGeneralSettings().
Creare una pagina dinamica per articoli e pagine WordPress
Un caso reale molto comune è creare una route dinamica Nuxt che intercetti gli URL provenienti da WordPress.
Puoi creare il file:
pages/[...slug].vue
E usare useNodeByUri() per recuperare qualsiasi contenuto tramite URI:
<script setup lang="ts">
const route = useRoute()
const { data: node } = await useNodeByUri({
uri: route.path
})
if (!node.value) {
throw createError({
statusCode: 404,
statusMessage: 'Contenuto non trovato'
})
}
useSeoMeta({
title: node.value.title
})
</script>
<template>
<article>
<h1>{{ node.title }}</h1>
<div v-sanitize-html="node.content" />
</article>
</template>
Questa soluzione è utile quando vuoi mantenere in WordPress la struttura degli URL e renderizzarla nel frontend Nuxt.
Rendering dei blocchi Gutenberg con WPNuxt
Una delle funzionalità più interessanti di WPNuxt è la possibilità di renderizzare i blocchi Gutenberg come componenti Vue.
Per farlo puoi installare il pacchetto dedicato:
pnpm add @wpnuxt/blocks
Poi aggiungerlo a nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@wpnuxt/core', '@wpnuxt/blocks'],
wpNuxt: {
wordpressUrl: 'https://example.com'
},
wpNuxtBlocks: {
imageDomains: ['example.com']
}
})
Nel template puoi usare il componente WPContent:
<script setup lang="ts">
const route = useRoute()
const { data: page } = await usePageByUri({
uri: route.path
})
</script>
<template>
<main>
<WPContent v-if="page" :node="page" />
</main>
</template>
WPContent è il componente consigliato per renderizzare contenuti WordPress: può gestire blocchi Gutenberg oppure HTML sanitizzato e intercetta i link interni per la navigazione client-side.
Personalizzare un blocco Gutenberg
Puoi anche sovrascrivere il rendering di un blocco specifico creando un componente Vue nella cartella:
components/blocks/
Per esempio, per personalizzare il blocco immagine di Gutenberg:
components/blocks/CoreImage.vue
<script setup lang="ts">
defineProps<{
block: {
attributes?: {
url?: string
alt?: string
caption?: string
className?: string
}
}
}>()
</script>
<template>
<figure :class="['wp-image-block', block.attributes?.className]">
<NuxtImg
v-if="block.attributes?.url"
:src="block.attributes.url"
:alt="block.attributes?.alt || ''"
loading="lazy"
/>
<figcaption
v-if="block.attributes?.caption"
v-sanitize-html="block.attributes.caption"
/>
</figure>
</template>
In questo modo puoi trasformare i blocchi Gutenberg in componenti realmente controllabili dal frontend.
È una soluzione molto potente per progetti dove il cliente vuole continuare a usare Gutenberg, ma il team di sviluppo vuole mantenere un frontend moderno, performante e coerente con il design system.
Creare query GraphQL personalizzate
WPNuxt include query di default, ma puoi aggiungerne di personalizzate.
Per esempio, immaginiamo di voler recuperare solo gli articoli in evidenza.
Crea il file:
extend/queries/FeaturedPosts.gql
query FeaturedPosts($limit: Int = 5) {
posts(first: $limit, where: { tag: "featured" }) {
nodes {
id
title
uri
excerpt
date
}
}
}
Dopo aver creato la query, rigenera i tipi:
pnpm nuxt prepare
A questo punto WPNuxt genera automaticamente il composable:
useFeaturedPosts()
Puoi usarlo così:
<script setup lang="ts">
const { data: featuredPosts } = await useFeaturedPosts({
limit: 3
})
</script>
<template>
<section>
<h2>Articoli in evidenza</h2>
<article v-for="post in featuredPosts" :key="post.id">
<NuxtLink :to="post.uri">
{{ post.title }}
</NuxtLink>
</article>
</section>
</template>
Le custom query sono utili per recuperare campi ACF, custom post type, tassonomie personalizzate, metadati SEO o dataset più leggeri rispetto alle query standard.
Esempio con custom post type
Supponiamo di avere un custom post type progetti registrato in WordPress ed esposto tramite WPGraphQL.
Puoi creare una query dedicata:
extend/queries/Projects.gql
query Projects($first: Int = 12) {
projects(first: $first) {
nodes {
id
title
uri
excerpt
featuredImage {
node {
sourceUrl
altText
}
}
}
}
}
Poi nel frontend:
<script setup lang="ts">
const { data: projects } = await useProjects({
first: 12
})
</script>
<template>
<section>
<h1>Progetti</h1>
<div class="grid">
<article v-for="project in projects" :key="project.id">
<NuxtImg
v-if="project.featuredImage?.node?.sourceUrl"
:src="project.featuredImage.node.sourceUrl"
:alt="project.featuredImage.node.altText || project.title"
/>
<h2>{{ project.title }}</h2>
<div v-sanitize-html="project.excerpt" />
<NuxtLink :to="project.uri">
Leggi il progetto
</NuxtLink>
</article>
</div>
</section>
</template>
Questo approccio è molto più pulito rispetto a una chiamata REST manuale, soprattutto quando il progetto cresce e i contenuti diventano più complessi.
SEO con WPNuxt
WPNuxt è particolarmente interessante per la SEO perché Nuxt può renderizzare le pagine lato server o generarle staticamente.
Per impostare title, meta description e dati Open Graph puoi usare useSeoMeta():
<script setup lang="ts">
const route = useRoute()
const { data: post } = await usePostByUri({
uri: route.path
})
const description = computed(() => {
return post.value?.excerpt
?.replace(/<[^>]+>/g, '')
.trim()
})
useSeoMeta({
title: post.value?.title,
description: description.value,
ogTitle: post.value?.title,
ogDescription: description.value,
ogImage: post.value?.featuredImage?.node?.sourceUrl,
ogType: 'article',
articlePublishedTime: post.value?.date,
articleModifiedTime: post.value?.modified
})
</script>
La documentazione di WPNuxt suggerisce proprio l’uso dei composable SEO di Nuxt, come useHead() e useSeoMeta(), per generare meta tag a partire dai dati WordPress.
Dati strutturati JSON-LD
Per migliorare la comprensione del contenuto da parte dei motori di ricerca, puoi aggiungere dati strutturati JSON-LD.
Esempio per un articolo:
<script setup lang="ts">
const route = useRoute()
const { data: post } = await usePostByUri({
uri: route.path
})
useHead({
script: [
{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.value?.title,
description: post.value?.excerpt?.replace(/<[^>]+>/g, '').trim(),
image: post.value?.featuredImage?.node?.sourceUrl,
datePublished: post.value?.date,
dateModified: post.value?.modified,
author: {
'@type': 'Person',
name: post.value?.author?.node?.name
}
})
}
]
})
</script>
Questa configurazione è utile per blog, magazine e siti editoriali dove ogni articolo deve avere una struttura semantica corretta.
Sitemap dinamica con WPNuxt
In un progetto headless devi assicurarti che Google possa scoprire tutti gli URL generati dal frontend Nuxt.
Puoi usare @nuxtjs/sitemap:
pnpm add -D @nuxtjs/sitemap
Configurazione base:
export default defineNuxtConfig({
modules: ['@wpnuxt/core', '@nuxtjs/sitemap'],
site: {
url: 'https://example.com'
}
})
Per generare URL dinamici da WordPress puoi creare una server route dedicata:
server/api/__sitemap__/urls.ts
import { defineSitemapEventHandler } from '#imports'
export default defineSitemapEventHandler(async () => {
const response = await fetch('https://example.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: `{
posts(first: 100) {
nodes {
uri
modified
}
}
pages(first: 100) {
nodes {
uri
modified
}
}
}`
})
})
const { data } = await response.json()
const posts = data?.posts?.nodes || []
const pages = data?.pages?.nodes || []
return [...posts, ...pages].map((node) => ({
loc: node.uri,
lastmod: node.modified
}))
})
In questo modo la sitemap viene alimentata dai contenuti reali presenti in WordPress.
Caching e performance
Uno dei problemi tipici di WordPress headless è il tempo di risposta del backend.
Se ogni pagina Nuxt deve interrogare WordPress a ogni richiesta, il frontend rischia di essere veloce solo in apparenza.
WPNuxt risolve questo problema con caching lato server.
Il comportamento predefinito include cache server-side e supporto SWR, cioè stale-while-revalidate.
Esempio di configurazione:
export default defineNuxtConfig({
modules: ['@wpnuxt/core'],
wpNuxt: {
wordpressUrl: 'https://example.com',
cache: {
enabled: true,
maxAge: 300,
swr: true
}
}
})
Con questa configurazione:
- la prima richiesta recupera i dati da WordPress
- la risposta viene salvata in cache
- le richieste successive ricevono la versione in cache
- il contenuto viene aggiornato in background
WPNuxt abilita più livelli di caching, tra cui cache server Nitro, cache client GraphQL e payload cache per evitare refetch durante l’hydration.
Quando usare SSR e quando usare SSG
Con WPNuxt puoi scegliere tra rendering server-side e generazione statica.
Usa SSR quando:
- i contenuti cambiano spesso
- hai molte pagine
- vuoi anteprime editoriali
- hai aree riservate
- ti serve aggiornamento rapido dei contenuti
Usa SSG quando:
- il sito è principalmente editoriale
- i contenuti cambiano raramente
- vuoi massimo rendimento da CDN
- vuoi ridurre i costi di hosting
- puoi rigenerare il sito a ogni pubblicazione
WPNuxt supporta anche la generazione statica completa, prerenderizzando le pagine a build time e producendo file HTML statici distribuibili su CDN o hosting statico.
Configurazione base per SSG:
export default defineNuxtConfig({
modules: ['@wpnuxt/core'],
nitro: {
preset: 'static'
},
wpNuxt: {
wordpressUrl: 'https://example.com'
}
})
Paginazione con WPNuxt
Per liste di articoli molto lunghe, puoi usare la paginazione cursor-based di WPGraphQL.
Crea una query:
extend/queries/PaginatedPosts.gql
query PaginatedPosts($first: Int, $after: String) {
posts(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
title
uri
excerpt
}
}
}
Dopo aver eseguito:
pnpm nuxt prepare
Puoi usare il composable generato:
<script setup lang="ts">
const {
data: posts,
pageInfo,
loadMore,
pending
} = await usePaginatedPosts({
first: 10
})
</script>
<template>
<section>
<article v-for="post in posts" :key="post.id">
<h2>{{ post.title }}</h2>
<NuxtLink :to="post.uri">Leggi articolo</NuxtLink>
</article>
<button
v-if="pageInfo?.hasNextPage"
:disabled="pending"
@click="loadMore"
>
Carica altri articoli
</button>
</section>
</template>
Quando una query include pageInfo, WPNuxt può generare supporto alla paginazione con pageInfo e loadMore().
Differenza tra WPNuxt e fetch manuale
Nel tuo articolo precedente abbiamo visto come recuperare dati WordPress con fetch nativo e REST API.
Quell’approccio è ottimo per capire le basi di WordPress headless con Nuxt.
WPNuxt, invece, è più adatto quando il progetto diventa più strutturato.
Con fetch manuale devi gestire:
- endpoint REST
- query string
- errori
- tipizzazione dei dati
- meta SEO
- caching
- paginazione
- custom post type
- blocchi Gutenberg
Con WPNuxt hai:
- composable generati automaticamente
- dati type-safe
- query GraphQL personalizzabili
- integrazione con Gutenberg
- caching già previsto
- struttura più scalabile
In sintesi: fetch manuale è ideale per imparare e per progetti semplici; WPNuxt è più indicato per progetti professionali, scalabili e mantenibili.
Best practice per usare WPNuxt in produzione
Per usare WPNuxt in produzione conviene seguire alcune buone pratiche.
Proteggi l’endpoint GraphQL
Durante lo sviluppo l’introspection pubblica è comoda, ma in produzione può essere preferibile limitarla o proteggerla.
Puoi usare una variabile d’ambiente:
WPNUXT_SCHEMA_AUTH_TOKEN=token-segreto
E configurarla in Nuxt:
export default defineNuxtConfig({
wpNuxt: {
wordpressUrl: 'https://example.com',
schemaAuthToken: process.env.WPNUXT_SCHEMA_AUTH_TOKEN
}
})
Usa canonical corretti
In un sito headless potresti avere lo stesso contenuto disponibile sia su WordPress sia sul frontend Nuxt.
Imposta sempre il canonical verso il frontend pubblico:
<script setup lang="ts">
const route = useRoute()
const config = useRuntimeConfig()
useHead({
link: [
{
rel: 'canonical',
href: `${config.public.siteUrl}${route.path}`
}
]
})
</script>
Ottimizza le immagini
Usa NuxtImg quando possibile e assicurati che le immagini WordPress siano servite in modo efficiente.
Esempio:
<NuxtImg
:src="post.featuredImage.node.sourceUrl"
:alt="post.featuredImage.node.altText || post.title"
width="1200"
height="630"
loading="lazy"
/>
Rigenera i tipi dopo ogni modifica alle query
Ogni volta che aggiungi o modifichi un file .gql, esegui:
pnpm nuxt prepare
Questo permette a WPNuxt di rigenerare composable e tipi TypeScript.
Esempio completo: pagina articolo SEO-friendly
Ecco un esempio realistico di pagina articolo con WPNuxt, meta tag, canonical e contenuto WordPress.
<script setup lang="ts">
const route = useRoute()
const config = useRuntimeConfig()
const { data: post } = await usePostByUri({
uri: route.path
})
if (!post.value) {
throw createError({
statusCode: 404,
statusMessage: 'Articolo non trovato'
})
}
const cleanDescription = computed(() => {
return post.value?.excerpt
?.replace(/<[^>]+>/g, '')
.trim()
})
useSeoMeta({
title: post.value.title,
description: cleanDescription.value,
ogTitle: post.value.title,
ogDescription: cleanDescription.value,
ogImage: post.value.featuredImage?.node?.sourceUrl,
ogType: 'article',
articlePublishedTime: post.value.date,
articleModifiedTime: post.value.modified,
twitterCard: 'summary_large_image'
})
useHead({
link: [
{
rel: 'canonical',
href: `${config.public.siteUrl}${route.path}`
}
],
script: [
{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.value.title,
description: cleanDescription.value,
image: post.value.featuredImage?.node?.sourceUrl,
datePublished: post.value.date,
dateModified: post.value.modified,
author: {
'@type': 'Person',
name: post.value.author?.node?.name
}
})
}
]
})
</script>
<template>
<article>
<header>
<h1>{{ post.title }}</h1>
<p v-if="post.date">
Pubblicato il {{ new Date(post.date).toLocaleDateString('it-IT') }}
</p>
<NuxtImg
v-if="post.featuredImage?.node?.sourceUrl"
:src="post.featuredImage.node.sourceUrl"
:alt="post.featuredImage.node.altText || post.title"
width="1200"
height="630"
loading="eager"
/>
</header>
<WPContent v-if="post" :node="post" />
</article>
</template>
Questo esempio rappresenta una base solida per un blog headless con WordPress e Nuxt.
Conclusione
WPNuxt è una soluzione molto interessante per chi vuole costruire un sito WordPress headless moderno, performante e scalabile.
Rispetto a un’integrazione manuale con REST API, offre un livello superiore di astrazione: composable type-safe, query GraphQL, supporto a Gutenberg, caching lato server, SEO avanzata e possibilità di lavorare con contenuti complessi in modo più ordinato.
Se il tuo obiettivo è creare un frontend Nuxt mantenendo WordPress come CMS editoriale, WPNuxt può diventare uno degli strumenti più efficaci per ridurre complessità, migliorare la developer experience e ottenere un sito più veloce e flessibile.
FAQ su WPNuxt
WPNuxt usa le REST API di WordPress?
No. WPNuxt usa GraphQL tramite il plugin WPGraphQL.
Questo consente di recuperare solo i campi necessari e generare tipi TypeScript a partire dallo schema.
WPNuxt funziona con Gutenberg?
Sì. Con il pacchetto @wpnuxt/blocks è possibile renderizzare i blocchi Gutenberg come componenti Vue.
WPNuxt è adatto alla SEO?
Sì. Usando Nuxt puoi generare pagine SSR o statiche, impostare meta tag dinamici, Open Graph, canonical, sitemap e dati strutturati.
Posso usare WPNuxt con custom post type?
Sì. Se il custom post type è esposto in WPGraphQL, puoi creare query personalizzate e generare composable dedicati.
WPNuxt è meglio delle REST API?
Dipende dal progetto.
Le REST API sono più semplici per integrazioni base. WPNuxt è più adatto a progetti Nuxt strutturati, con TypeScript, Gutenberg, custom query e necessità SEO avanzate.