Традиційний 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 фронтенду headless (клієнтські бібліотеки 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 ядра)acf (інтеграція ACF)Щоб компонент Next.js рендерив блок Hero, йому може знадобитися 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 є основним фреймворком WordPress headless.
“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.