Традиционный WordPress: один стек. WordPress PHP рендерит всё — фронтенд-страницы, админские экраны, REST API. Ваша тема обрабатывает как редактирование контента (через админку), так и отображение на фронтенде (через PHP-шаблоны).
Headless WordPress: два стека. WordPress обрабатывает редактирование контента и хранение данных. Отдельный фронтенд на JavaScript (работающий на Vercel, Netlify, Cloudflare Pages и т.д.) получает контент из WordPress через REST API или GraphQL и рендерит страницы независимо.
«` Традиционный: [ WordPress ] → Контент → Фронтенд (PHP шаблоны) ↑ ↑ └──── тот же сервер ─────────┘
Headless: [ WordPress ] → Контент → [ JavaScript Фронтенд ] (CMS) (API) (Next.js / Astro / и т.д.) «`
Для большинства сайтов на WordPress традиционный WordPress по-прежнему является правильным выбором. Headless — это правильный выбор, когда вам нужны его специфические преимущества и вы принимаете сложность.
Пользовательские поля — одна из самых больших точек трения в headless WordPress. Вот почему.
Ядро WordPress имеет стандартные поля — заголовок, контент, аннотация, изображение, автор, дата. Эти поля хорошо определены, последовательны на всех сайтах WordPress и полностью поддерживаются SDK для фронтенда (клиентские библиотеки wp-rest-api, WPGraphQL и т.д.).
Пользовательские поля определяются плагинами. ACF добавляет свои собственные поля, хранящиеся по своему собственному методу, доступные через свой собственный API. Meta Box делает то же самое. Pods, Toolset, CMB2, Carbon Fields — каждый имеет свой подход.
SDK для фронтенда не знают о пользовательских полях, потому что не могут — определения находятся в коде плагина, а не в ядре WordPress.
TypeScript не может вывести типы пользовательских полей из ответа API WordPress. Вызов fetch('/wp-json/wp/v2/posts/123') возвращает общий объект Post плюс свойство «ACF» с любыми полями, которые добавил плагин. TypeScript видит это как any или Record.
Результат: data.custom_fields.hero_title компилируется нормально, даже если поле называется hero_headline. Ошибки в написании становятся ошибками времени выполнения.
Разработчик добавляет новую группу полей на своем локальном WordPress. Он отправляет код на staging. Staging WordPress еще не имеет этой группы полей. Фронтенд-код, который ожидает новое поле, ломается.
Без синхронизации схем headless WordPress становится кошмаром для отладки.
REST API WordPress экспонирует пользовательские поля непоследовательно:
meta (поведение по умолчанию WP core)acf (интеграция ACF)Для компонента Next.js, чтобы отрендерить блок Героя, может потребоваться 3 отдельных вызова API: один для поста, один для изображения, один для связанного поста, упомянутого в реляционном поле. Это медленно и сложно.
GraphQL (через WPGraphQL) лучше подходит для headless, потому что позволяет вам запрашивать глубоко вложенные данные за один запрос. Но WPGraphQL не знает о пользовательских полях — вам нужен отдельный плагин «WPGraphQL для ACF», чтобы устранить разрыв, и он поддерживается независимо, поэтому отстает от обновлений ACF.
Field Forge был разработан с учетом headless WordPress. Он решает каждую из вышеупомянутых проблем.
Вместо wp_postmeta, Field Forge хранит значения полей в выделенной индексированной таблице. Ответы API, которые извлекают пользовательские поля, значительно быстрее — в 3–10 раз быстрее на больших сайтах. Для headless-рабочих процессов, где фронтенд получает контент из WordPress при каждой сборке (или каждом запросе для SSR), это имеет значение.
Функция хранения в пользовательской таблице →
Field Forge автоматически генерирует .d.ts определения TypeScript для каждой группы полей. Скачайте файл, зафиксируйте его в вашем репозитории фронтенда, получите полную безопасность типов:
«`typescript import type { HeroSectionFields } from ‘@/types/fieldforge’;
function Hero({ data }: { data: HeroSectionFields }) { return
; // Автозаполнение TypeScript + проверка ошибок } «`
Типы обновляются автоматически, когда вы редактируете группы полей. Повторно запустите экспорт TypeScript для синхронизации.
Функция генерации TypeScript →
Когда Field Forge и WPGraphQL оба активны, Field Forge автоматически регистрирует типы GraphQL для каждой группы полей при активации. Никакой конфигурации. Пользовательские поля становятся доступными для запросов немедленно:
«graphql query GetPage { page(id: "home", idType: URI) { title fieldforge { heroSection { title subtitle backgroundImage { sourceUrl } } } } } «
Нет необходимости в отдельном плагине WPGraphQL для ACF.
Field Forge экспонирует значения пользовательских полей на основных конечных точках REST под свойством fieldforge:
«`json GET /wp-json/wp/v2/posts/123
{ «id»: 123, «title»: { «rendered»: «Наши услуги» }, «fieldforge»: { «service_list»: [ { «name»: «Веб-дизайн», «description»: «…» }, { «name»: «SEO», «description»: «…» } ] } } «`
Один вызов API возвращает пост + все пользовательские поля. Нет проблемы N+1.
Local JSON Sync Field Forge сохраняет группы полей как JSON-файлы в директории вашей темы. Зафиксируйте в git. Развертывание между окружениями. Dev, staging и production всегда имеют одну и ту же схему.
Вот полное руководство по настройке Next.js с Field Forge.
«bash npx create-next-app@latest my-headless-site --typescript --app cd my-headless-site «
Создайте 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]; } «`
Скачайте .d.ts файл из админки Field Forge: Field Forge → Инструменты → Экспорт TypeScript → Скачать.
Сохраните в 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}
); } «`
Это минимальная настройка Next.js + Field Forge. Автозаполнение TypeScript работает. Рефакторинг имен полей безопасен. Сборка не проходит, если вы ошиблись в написании свойства.
generateStaticParamsДля статической генерации сайта (самый быстрый вариант) получите все страницы во время сборки:
«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 будет предварительно рендерить каждую страницу как статический HTML во время сборки.
Astro отлично подходит для контентных сайтов с headless WordPress.
«bash npm create astro@latest my-site cd my-site «
Создайте 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 && ( )} «`
Статическая генерация Astro хорошо работает с REST API Field Forge — одна сборка, весь контент извлечен, статический HTML вывод.
Для разработчиков Vue Nuxt является основным фреймворком для headless WordPress.
«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}); «`
Для фанатов 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} «`
Для сайтов, нуждающихся в глубоких вложенных запросах, GraphQL более эффективен, чем 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; } «`
Один запрос извлекает всю страницу, включая пользовательские поля. Нет проблемы N+1.
Стоит ли использовать REST или GraphQL с headless WordPress? Зависит от фронтенда и сложности. REST проще настраивать. GraphQL более эффективен для глубоко вложенных запросов и уменьшает избыточную выборку. Для небольших сайтов — REST. Для сайтов со сложными структурами контента — GraphQL.
Как мне обрабатывать предпросмотры в headless WordPress? Предпросмотр — одна из самых сложных частей headless. Варианты: режим предпросмотра Next.js (извлекает черновой контент с секретным токеном), плагин предпросмотра Gatsby или индивидуальные триггеры вебхуков WordPress → фронтенд.
Как часто мне следует пересобирать мой статический сайт? Зависит от частоты обновления контента. Для ежедневно обновляемого контента — ночные сборки. Для менее частых обновлений — сборки, инициируемые вебхуками (WordPress публикует пост → вебхук инициирует сборку).
Могу ли я редактировать контент через headless фронтенд? Да, через REST API WordPress или мутации WPGraphQL. Но для редакционных рабочих процессов большинство команд редактируют в админке WordPress и позволяют фронтенду читать.
Как мне обрабатывать формы в headless WordPress? Варианты: 1) Form Forge отправляет данные в REST API WordPress, 2) Сервис форм третьих сторон (Formspree, Netlify Forms), 3) Прямая интеграция API с сервисами форм. Для большинства случаев Form Forge + REST API — самый простой вариант.
Что насчет загрузки медиа? Headless фронтенды обычно все еще используют медиатеку WordPress для хранения. Фронтенд получает URL изображений из ответов API. Для контента, загружаемого пользователями, POST на конечную точку REST медиа WordPress.
Получите Field Forge — от $35 в год →
Бесплатная версия включает REST API и базовые типы. Платные планы открывают авто-генерацию TypeScript, авто-регистрацию GraphQL и продвинутые инструменты для headless.