WordPress tradicional: una pila. WordPress PHP renderiza todo: páginas frontend, pantallas de administración, REST API. Tu tema maneja tanto la edición de contenido (a través del administrador) como la visualización en el frontend (a través de plantillas PHP).
WordPress headless: dos pilas. WordPress maneja la edición de contenido y el almacenamiento de datos. Un frontend JavaScript separado (que se ejecuta en Vercel, Netlify, Cloudflare Pages, etc.) obtiene contenido de WordPress a través de REST API o GraphQL y renderiza páginas de forma independiente.
“` Tradicional: [ WordPress ] → Contenido → Frontend (plantillas PHP) ↑ ↑ └──── mismo servidor ─────────┘
Headless: [ WordPress ] → Contenido → [ Frontend JavaScript ] (CMS) (API) (Next.js / Astro / etc.) “`
Para la mayoría de los sitios de WordPress, WordPress tradicional sigue siendo la opción correcta. WordPress headless es la opción correcta cuando necesitas sus ventajas específicas y aceptas la complejidad.
Los campos personalizados son uno de los mayores puntos de fricción en WordPress headless. Aquí está el porqué.
El núcleo de WordPress tiene campos estándar: título, contenido, extracto, imagen destacada, autor, fecha. Estos están bien definidos, son consistentes en todos los sitios de WordPress y son totalmente compatibles con los SDKs de frontend headless (bibliotecas de clientes wp-rest-api, WPGraphQL, etc.).
Los campos personalizados son definidos por plugins. ACF añade sus propios campos, almacenados de su propia manera, expuestos a través de su propia API. Meta Box hace lo mismo. Pods, Toolset, CMB2, Carbon Fields: cada uno tiene su propio enfoque.
Los SDKs de frontend no conocen los campos personalizados porque no pueden: las definiciones viven en el código del plugin, no en el núcleo de WordPress.
TypeScript no puede inferir los tipos de campos personalizados de una respuesta de API de WordPress. Una llamada fetch('/wp-json/wp/v2/posts/123') devuelve un objeto Post genérico más una propiedad “ACF” con los campos que el plugin añadió. TypeScript ve esto como any o Record.
El resultado: data.custom_fields.hero_title compila bien incluso si el campo se llama hero_headline. Los errores tipográficos se convierten en errores en tiempo de ejecución.
Un desarrollador añade un nuevo grupo de campos en su WordPress local. Empuja el código a staging. El WordPress de staging aún no tiene ese grupo de campos. El código del frontend que espera el nuevo campo se rompe.
Sin sincronización de esquemas, WordPress headless se convierte en una pesadilla de depuración.
La REST API de WordPress expone los campos personalizados de manera inconsistente:
meta (comportamiento predeterminado del núcleo de WP)acf (integración de ACF)Para que un componente de Next.js renderice un bloque Hero, podría necesitar 3 llamadas a la API separadas: una para la publicación, una para la imagen destacada, una para la publicación relacionada referenciada en un campo de relación. Esto es lento y complejo.
GraphQL (a través de WPGraphQL) es una mejor opción para headless porque te permite consultar datos profundamente anidados en una sola solicitud. Pero WPGraphQL no conoce los campos personalizados: necesitas un plugin separado “WPGraphQL para ACF” para cerrar la brecha, y se mantiene de forma independiente, por lo que se queda atrás en las actualizaciones de ACF.
Field Forge fue diseñado pensando en WordPress headless. Aborda cada punto de dolor mencionado anteriormente.
En lugar de wp_postmeta, Field Forge almacena los valores de los campos en una tabla indexada dedicada. Las respuestas de API que obtienen campos personalizados son drásticamente más rápidas: de 3 a 10 veces más rápidas en sitios grandes. Para flujos de trabajo headless donde el frontend obtiene contenido de WordPress en cada construcción (o cada solicitud para SSR), esto es importante.
Función de almacenamiento en tabla personalizada →
Field Forge genera automáticamente definiciones de TypeScript .d.ts para cada grupo de campos. Descarga el archivo, compítelo en tu repositorio frontend, obtén seguridad de tipos completa:
“`typescript import type { HeroSectionFields } from ‘@/types/fieldforge’;
function Hero({ data }: { data: HeroSectionFields }) { return
; // Autocompletado de TypeScript + verificación de errores } “`
Los tipos se actualizan automáticamente cuando editas grupos de campos. Vuelve a ejecutar la exportación de TypeScript para sincronizar.
Función de generación de TypeScript →
Cuando Field Forge y WPGraphQL están activos, Field Forge registra automáticamente tipos de GraphQL para cada grupo de campos al activarse. Cero configuración. Los campos personalizados se vuelven consultables de inmediato:
“graphql query GetPage { page(id: "home", idType: URI) { title fieldforge { heroSection { title subtitle backgroundImage { sourceUrl } } } } } “
No se necesita un plugin separado WPGraphQL para ACF.
Función de generación de GraphQL →
Field Forge expone los valores de los campos personalizados en los endpoints REST principales bajo una propiedad fieldforge:
“`json GET /wp-json/wp/v2/posts/123
{ “id”: 123, “title”: { “rendered”: “Nuestros Servicios” }, “fieldforge”: { “service_list”: [ { “name”: “Diseño Web”, “description”: “…” }, { “name”: “SEO”, “description”: “…” } ] } } “`
Una llamada a la API devuelve la publicación + todos los campos personalizados. Sin problema N+1.
La Sincronización de JSON Local de Field Forge guarda grupos de campos como archivos JSON en el directorio de tu tema. Compítelo en git. Despliega en diferentes entornos. Desarrollo, staging y producción siempre tienen el mismo esquema.
Aquí tienes una guía completa de configuración para Next.js con Field Forge.
“bash npx create-next-app@latest my-headless-site --typescript --app cd my-headless-site “
Crea lib/wordpress.ts:
“`typescript const WP_API_URL = process.env.WP_API_URL || ‘https://wp.example.com/wp-json’;
export async function fetchPost(slug: string) { const res = await fetch(${WP_API_URL}/wp/v2/posts?slug=${slug}); const posts = await res.json(); return posts[0]; }
export async function fetchPage(slug: string) { const res = await fetch(${WP_API_URL}/wp/v2/pages?slug=${slug}); const pages = await res.json(); return pages[0]; } “`
Descarga el archivo .d.ts desde el administrador de Field Forge: Field Forge → Herramientas → Exportación de TypeScript → Descargar.
Guarda en types/fieldforge.d.ts:
“`typescript export interface WPImage { id: number; url: string; alt: string; sizes: { thumbnail: string; medium: string; large: string; full: string; }; }
export interface HeroSectionFields { title: string; subtitle: string; background_image: WPImage; cta_button: { text: string; url: string; }; }
export interface PageWithFields { id: number; title: { rendered: string }; fieldforge: { hero_section?: HeroSectionFields; }; } “`
app/[slug]/page.tsx:
“`typescript import { fetchPage } from ‘@/lib/wordpress’; import type { PageWithFields, HeroSectionFields } from ‘@/types/fieldforge’;
export default async function Page({ params }: { params: { slug: string } }) { const page: PageWithFields = await fetchPage(params.slug);
return (
{page.fieldforge.hero_section && ( )} ); }
function Hero({ data }: { data: HeroSectionFields }) { return (
{data.subtitle}
); } “`
Esa es una configuración mínima de Next.js + Field Forge. El autocompletado de TypeScript funciona. Refactorizar nombres de campos es seguro. La construcción falla si cometes un error tipográfico en una propiedad.
generateStaticParamsPara la generación de sitios estáticos (opción más rápida), obtén todas las páginas en el momento de la construcción:
“typescript export async function generateStaticParams() { const res = await fetch(${WP_API_URL}/wp/v2/pages`); const pages = await res.json();
return pages.map((page: any) => ({ slug: page.slug, })); } “`
Next.js pre-renderizará cada página como HTML estático en el momento de la construcción.
Astro es excelente para sitios de contenido con WordPress headless.
“bash npm create astro@latest my-site cd my-site “
Crea src/lib/wordpress.ts:
“`typescript const WP_API_URL = import.meta.env.WP_API_URL;
export async function getPages() { const res = await fetch(${WP_API_URL}/wp/v2/pages); return res.json(); }
export async function getPageBySlug(slug: string) { const res = await fetch(${WP_API_URL}/wp/v2/pages?slug=${slug}); const [page] = await res.json(); return page; } “`
src/pages/[slug].astro:
“`astro
import { getPages, getPageBySlug } from ‘@/lib/wordpress’; import type { PageWithFields } from ‘@/types/fieldforge’; import Hero from ‘@/components/Hero.astro’;
export async function getStaticPaths() { const pages = await getPages(); return pages.map((page: any) => ({ params: { slug: page.slug }, props: { page }, })); }
const { page } = Astro.props as { page: PageWithFields };
{page.fieldforge.hero_section && ( )} “`
La generación estática de Astro funciona bien con la REST API de Field Forge: una construcción, todo el contenido obtenido, salida de HTML estático.
Para desarrolladores de Vue, Nuxt es el marco headless de WordPress por excelencia.
“bash npx nuxi@latest init my-site cd my-site “
server/api/page/[slug].get.ts:
“`typescript export default defineEventHandler(async (event) => { const { slug } = getRouterParams(event); const config = useRuntimeConfig();
const response = await $fetch( ${config.wpApiUrl}/wp/v2/pages?slug=${slug} );
return response[0]; }); “`
pages/[slug].vue:
“`vue
import type { PageWithFields } from '@/types/fieldforge';
const route = useRoute(); const { data: page } = await useFetch(/api/page/${route.params.slug}); “`
Para los fans de Svelte:
“`typescript // src/routes/[slug]/+page.server.ts import type { PageServerLoad } from ‘./$types’; import type { PageWithFields } from ‘$lib/types/fieldforge’;
export const load: PageServerLoad = async ({ params, fetch }) => { const res = await fetch(${WP_API_URL}/wp/v2/pages?slug=${params.slug}); const pages = await res.json(); const page: PageWithFields = pages[0];
return { page }; }; “`
“`svelte import type { PageData } from './$types'; import Hero from '$lib/components/Hero.svelte';
export let data: PageData;
{#if data.page.fieldforge.hero_section} {/if} “`
Para sitios que necesitan consultas profundamente anidadas, GraphQL es más eficiente que REST:
“typescript async function fetchHomepage() { const query = query GetHomepage { page(id: “home”, idType: URI) { title fieldforge { heroSection { title subtitle backgroundImage { sourceUrl altText } ctaButton { text url } } } } } `;
const res = await fetch(${WP_API_URL}/graphql, { method: ‘POST’, headers: { ‘Content-Type’: ‘application/json’ }, body: JSON.stringify({ query }), });
const { data } = await res.json(); return data.page; } “`
Una solicitud obtiene toda la página incluyendo campos personalizados. Sin problema N+1.
¿Debería usar REST o GraphQL con WordPress headless? Depende del frontend y la complejidad. REST es más simple de configurar. GraphQL es más eficiente para consultas profundamente anidadas y reduce la sobrecarga. Para sitios pequeños, REST. Para sitios con estructuras de contenido complejas, GraphQL.
¿Cómo manejo las vistas previas en WordPress headless? La vista previa es una de las partes más difíciles de headless. Opciones: modo de vista previa de Next.js (obtiene contenido borrador con un token secreto), plugin de vista previa de Gatsby, o triggers de webhook personalizados de WordPress → frontend.
¿Con qué frecuencia debo reconstruir mi sitio estático? Depende de la frecuencia de actualización del contenido. Para contenido actualizado diariamente, construcciones nocturnas. Para actualizaciones menos frecuentes, construcciones activadas por webhook (WordPress publica una publicación → el webhook activa una construcción).
¿Puedo editar contenido a través del frontend headless? Sí, a través de la REST API de WordPress o mutaciones de WPGraphQL. Pero para flujos de trabajo editoriales, la mayoría de los equipos editan en el administrador de WordPress y permiten que el frontend lea.
¿Cómo manejo formularios en WordPress headless? Opciones: 1) Form Forge publica en la REST API de WordPress, 2) Servicio de formularios de terceros (Formspree, Netlify Forms), 3) Integración directa de API con servicios de formularios. Para la mayoría de los casos, Form Forge + REST API es lo más simple.
¿Qué pasa con las cargas de medios? Los frontends headless típicamente aún usan la biblioteca de medios de WordPress para almacenamiento. El frontend obtiene URLs de imágenes de las respuestas de la API. Para contenido subido por el usuario, POST a la endpoint REST de medios de WordPress.
Obtén Field Forge — desde $35/año →
La versión gratuita incluye REST API y tipos básicos. Los planes de pago desbloquean la auto-generación de TypeScript, la auto-registro de GraphQL y herramientas avanzadas para headless.