🎨 Sysprovider Code
Sysprovider LogoWiki
🇪🇸Hosting español para ecommerce

Implementación de Headless WordPress con Next.js 14

Actualizado el 12 de mayo de 2026

Imagina un WordPress que entrega contenido a través de una API moderna, mientras que el frontend es una aplicación React ultrarrápida y completamente independiente. Eso es exactamente lo que ofrece la combinación de Headless WordPress con Next.js 14. Este enfoque, basado en la arquitectura JAMstack, permite desacoplar la gestión de contenido del motor de renderizado, ofreciendo una flexibilidad, seguridad y rendimiento sin precedentes.

En este artículo, exploraremos en profundidad cómo implementar esta arquitectura, desde los fundamentos hasta la configuración práctica, cubriendo tanto la WP REST API como GraphQL como fuentes de datos.

¿Qué es Headless WordPress y por qué con Next.js 14?

WordPress tradicional funciona como un monolito: el backend (PHP + MySQL) genera el HTML que ves en el frontend. En un headless WordPress, el backend se separa completamente. WordPress actúa únicamente como un CMS headless (sistema de gestión de contenidos) que expone sus datos a través de APIs (REST o GraphQL). El frontend, en este caso Next.js 14, consume esos datos y los renderiza en el cliente o servidor.

Next.js 14 es el framework React ideal para esta tarea por varias razones:

  • Server Components: Por defecto, los componentes son del lado del servidor, lo que reduce el JavaScript enviado al cliente y mejora el SEO.
  • App Router: Un sistema de enrutamiento basado en el sistema de archivos, más intuitivo y potente que el anterior Pages Router.
  • Server Actions: Permite ejecutar código del servidor directamente desde componentes del cliente, ideal para formularios y mutaciones en WordPress.
  • Rendimiento nativo: Con funciones como fetch extendido, revalidación de datos y streaming, la velocidad es excepcional.

[INFO] La arquitectura JAMstack (JavaScript, APIs, Markup) encaja perfectamente aquí: JavaScript (Next.js), APIs (WP REST API o GraphQL) y Markup pre-renderizado.

Preparando el Backend: Configuración de Headless WordPress

Antes de escribir código en Next.js, debemos preparar WordPress para que funcione como un CMS headless.

1. Instalación y configuración básica

Necesitas una instalación de WordPress (local o en un servidor). Asegúrate de tener las permalinks configuradas como "Nombre de la entrada" (Settings > Permalinks > Post name).

2. Plugins esenciales para el modo Headless

Para exponer los datos de forma eficiente y segura, instala estos plugins:

  • WPGraphQL: Si optas por GraphQL, este plugin es indispensable. Expone un endpoint /graphql con un esquema completo de tu sitio.
  • Advanced Custom Fields (ACF): Para agregar campos personalizados a tus entradas, páginas o taxonomías. WPGraphQL tiene soporte nativo para ACF.
  • Custom Post Type UI (CPTUI): Para crear tipos de contenido personalizados (por ejemplo, "Proyectos", "Testimonios").
  • WP REST API Menus: Si necesitas exponer los menús de navegación a través de la WP REST API (por defecto no están incluidos).

[WARNING] No desactives el frontend de WordPress (el tema) a menos que estés 100% seguro. Puedes usar un tema mínimo como "Twenty Twenty-Three" y redirigir todo el tráfico a tu frontend de Next.js.

3. Configuración de CORS

Para que tu frontend (por ejemplo, http://localhost:3000) pueda consumir la API, debes permitir CORS. Puedes hacerlo mediante un plugin como "CORS" o agregando código en wp-config.php:

// En wp-config.php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");

Construyendo el Frontend con Next.js 14

Ahora pasamos a la parte divertida: crear la aplicación Next.js que consumirá los datos de WordPress.

1. Crear el proyecto

Abre tu terminal y ejecuta:

npx create-next-app@latest headless-wp-nextjs --typescript --tailwind --app
cd headless-wp-nextjs

Esto crea un proyecto con TypeScript, Tailwind CSS y el nuevo App Router.

2. Configurar variables de entorno

Crea un archivo .env.local en la raíz del proyecto:

WORDPRESS_API_URL=http://tusitio.local/wp-json/wp/v2
WORDPRESS_GRAPHQL_URL=http://tusitio.local/graphql

3. Consumir la WP REST API

Vamos a crear una función helper para obtener posts. Crea una carpeta lib y dentro un archivo wordpress.ts:

// lib/wordpress.ts
const API_URL = process.env.WORDPRESS_API_URL;

export async function getPosts() {
  const res = await fetch(`${API_URL}/posts?_embed`);
  if (!res.ok) throw new Error('Failed to fetch posts');
  return res.json();
}

export async function getPost(slug: string) {
  const res = await fetch(`${API_URL}/posts?slug=${slug}&_embed`);
  if (!res.ok) throw new Error('Failed to fetch post');
  const posts = await res.json();
  return posts[0];
}

[TIP] Usa _embed para incluir datos relacionados como imágenes destacadas, autor y taxonomías en una sola petición.

Ahora, en tu app/page.tsx, consume estos datos:

// app/page.tsx
import { getPosts } from '@/lib/wordpress';

export default async function Home() {
  const posts = await getPosts();

  return (
    <main className="container mx-auto p-4">
      <h1 className="text-4xl font-bold mb-8">Últimos Posts</h1>
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
        {posts.map((post: any) => (
          <article key={post.id} className="border rounded-lg p-4 shadow">
            <img 
              src={post._embedded?.['wp:featuredmedia']?.[0]?.source_url || '/placeholder.jpg'} 
              alt={post.title.rendered}
              className="w-full h-48 object-cover rounded mb-4"
            />
            <h2 className="text-xl font-semibold">{post.title.rendered}</h2>
            <div dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }} />
          </article>
        ))}
      </div>
    </main>
  );
}

Integración con GraphQL

Si prefieres usar GraphQL (más flexible y eficiente), aquí tienes cómo hacerlo.

1. Configurar el cliente GraphQL

Instala la librería graphql-request:

npm install graphql-request graphql

Crea un archivo lib/graphql.ts:

// lib/graphql.ts
import { GraphQLClient } from 'graphql-request';

const endpoint = process.env.WORDPRESS_GRAPHQL_URL!;
const client = new GraphQLClient(endpoint);

export async function fetchPosts() {
  const query = `
    query GetPosts {
      posts(first: 10) {
        nodes {
          id
          title
          slug
          excerpt
          featuredImage {
            node {
              sourceUrl
            }
          }
        }
      }
    }
  `;
  return client.request(query);
}

2. Usar en una página

Modifica app/page.tsx para usar GraphQL:

import { fetchPosts } from '@/lib/graphql';

export default async function Home() {
  const data = await fetchPosts();
  const posts = data.posts.nodes;

  return (
    <main className="container mx-auto p-4">
      <h1 className="text-4xl font-bold mb-8">Posts con GraphQL</h1>
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
        {posts.map((post: any) => (
          <article key={post.id} className="border rounded-lg p-4 shadow">
            {post.featuredImage?.node?.sourceUrl && (
              <img 
                src={post.featuredImage.node.sourceUrl} 
                alt={post.title}
                className="w-full h-48 object-cover rounded mb-4"
              />
            )}
            <h2 className="text-xl font-semibold">{post.title}</h2>
            <div dangerouslySetInnerHTML={{ __html: post.excerpt }} />
          </article>
        ))}
      </div>
    </main>
  );
}

Generación de Rutas Dinámicas (SSG)

Una de las ventajas del headless WordPress con Next.js es la generación de sitios estáticos (SSG). Vamos a crear páginas individuales para cada post.

1. Página dinámica para posts

Crea una carpeta app/posts/[slug]/page.tsx:

import { getPost, getPosts } from '@/lib/wordpress';

export async function generateStaticParams() {
  const posts = await getPosts();
  return posts.map((post: any) => ({
    slug: post.slug,
  }));
}

export default async function PostPage({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);

  return (
    <article className="container mx-auto p-4 max-w-3xl">
      <h1 className="text-4xl font-bold mb-4">{post.title.rendered}</h1>
      {post._embedded?.['wp:featuredmedia']?.[0]?.source_url && (
        <img 
          src={post._embedded['wp:featuredmedia'][0].source_url} 
          alt={post.title.rendered}
          className="w-full h-64 object-cover rounded mb-8"
        />
      )}
      <div 
        className="prose max-w-none"
        dangerouslySetInnerHTML={{ __html: post.content.rendered }} 
      />
    </article>
  );
}

[INFO] generateStaticParams genera todas las rutas posibles en tiempo de compilación. Next.js pre-renderizará cada página estáticamente, ofreciendo un rendimiento máximo.

Manejo de Menús y Opciones del Sitio

Los menús en WordPress no se exponen por defecto en la REST API. Para solucionarlo, instala el plugin WP REST API Menus o usa GraphQL que sí los expone.

Con GraphQL:

query GetMenu {
  menuItems(where: {location: PRIMARY}) {
    nodes {
      id
      label
      url
      path
      parentId
    }
  }
}

Luego, crea un componente Navbar.tsx que consuma esta query y renderice el menú de navegación.

Optimización de Imágenes con Next.js

Las imágenes de WordPress suelen ser grandes. Next.js ofrece un componente Image que optimiza automáticamente las imágenes. Sin embargo, para imágenes externas (como las de WordPress), debes configurar los dominios permitidos en next.config.js:

// next.config.js
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'http',
        hostname: 'tusitio.local',
        port: '',
        pathname: '/wp-content/**',
      },
    ],
  },
};

Luego, usa el componente Image de Next.js:

import Image from 'next/image';

<Image 
  src={post._embedded['wp:featuredmedia'][0].source_url}
  alt={post.title.rendered}
  width={1200}
  height={600}
  className="w-full h-64 object-cover rounded"
/>

Despliegue y Consideraciones Finales

Para desplegar tu aplicación, puedes usar Vercel (recomendado por los creadores de Next.js) o Netlify. Ambos soportan funciones serverless y construcción automática.

Pasos para desplegar en Vercel:

  1. Conecta tu repositorio de Git (GitHub, GitLab, Bitbucket).
  2. Configura las variables de entorno en Vercel (WORDPRESS_API_URL, WORDPRESS_GRAPHQL_URL).
  3. Despliega. Vercel detectará automáticamente que es un proyecto Next.js.

[WARNING] Asegúrate de que tu WordPress esté accesible públicamente o usa un túnel como ngrok durante el desarrollo. En producción, considera usar un CDN como Cloudflare para cachear las peticiones a la API.

Conclusión

La implementación de Headless WordPress con Next.js 14 no solo moderniza tu stack tecnológico, sino que también mejora drásticamente el rendimiento, la seguridad y la experiencia de desarrollo. Al desacoplar el frontend del backend, obtienes:

  • Rendimiento superior: Sitios estáticos o renderizados en servidor con tiempos de carga mínimos.
  • Flexibilidad total: Puedes usar cualquier framework frontend (React, Vue, Svelte) sin afectar WordPress.
  • Seguridad mejorada: Al no exponer PHP en el frontend, reduces la superficie de ataque.
  • Escalabilidad: Puedes escalar el frontend y backend de forma independiente.

Con la WP REST API o GraphQL como puente, y las potentes características de Next.js 14 como Server Components y App Router, tienes en tus manos una arquitectura JAMstack robusta y preparada para el futuro.

¿Listo para dar el salto? Empieza con un proyecto pequeño, migra un blog o un sitio corporativo, y experimenta la libertad del headless WordPress.

¿Necesitas ayuda?Son dos de nuestros técnicos, Agustín y Mikel, y están disponibles para resolver cualquier problema.

Hablar con ellos ahora
Agustín y Mikel