Как подключить React к WordPress через API
Полгода назад передо мной встала задача — превратить обычный WordPress-сайт клиента в быстрое SPA-приложение, сохранив привычную для контент-менеджеров админку. Делюсь пошаговым решением, которое я обкатал на трёх проектах.
Почему именно этот подход
WordPress REST API появился в ядре с версии 4.7, и это освободило разработчиков от шаблонов PHP. Теперь WordPress можно использовать как headless CMS — только для хранения и выдачи данных, а фронтенд полностью отдать React.
Шаг 1: Подготовка WordPress
Первым делом я убеждаюсь, что REST API включен (по умолчанию он активен). Проверить это просто — открываем в браузере:
https://ваш-сайт.ru/wp-json/wp/v2/posts
Если видите JSON с постами — всё работает. Если нет, проверьте плагины безопасности, которые могут блокируют API.
Важный момент, который я всегда настраиваю — CORS. Без этого React-приложение с другого домена не получит доступ к данным. Добавляю в functions.php темы:
function add_cors_http_header(){
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
}
add_action('init', 'add_cors_http_header');
Для продакшена рекомендую заменить * на конкретный домен вашего React-приложения — это безопаснее.
Шаг 2: Создание React-приложения
Использую Vite для создания React приложения:
npm create vite@latest my-wp-app -- --template react
cd my-wp-app
npm install
npm install axios
Axios беру вместо fetch — удобнее обрабатывать запросы и ошибки.
Шаг 3: Получение данных из WordPress
Создаю отдельный файл для API-запросов — так проект остаётся чистым и масштабируемым app/api/wordpress.js:
import axios from 'axios';
const API_URL = 'https://site.ru/wp-json/wp/v2';
export const getPosts = async () => {
try {
const response = await axios.get(`${API_URL}/posts?_embed`);
return response.data;
} catch (error) {
console.error('Ошибка загрузки:', error);
throw error;
}
};
export const getPostBySlug = async (slug) => {
const response = await axios.get(`${API_URL}/posts?slug=${slug}&_embed`);
return response.data[0];
};
Параметр _embed — моя любимая фишка. Он подгружает связанные данные (изображения, авторов, категории) одним запросом, избавляя от лишних обращений к серверу.
Шаг 4: Компонент для вывода постов
Создаем компонент app/components/PostList.jsx:
import { useState, useEffect } from 'react';
import { getPosts } from '../api/wordpress';
function PostList() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchPosts = async () => {
try {
const data = await getPosts();
setPosts(data);
} catch (err) {
setError('Не удалось загрузить статьи');
} finally {
setLoading(false);
}
};
fetchPosts();
}, []);
if (loading) return <div className="loader">Загрузка...</div>;
if (error) return <div className="error">{error}</div>;
return (
<div className="post-list">
{posts.map((post) => {
const imageUrl = post._embedded?.['wp:featuredmedia']?.[0]?.source_url;
return (
<article key={post.id} className="post-card">
{imageUrl && <img src={imageUrl} alt={post.title.rendered} />}
<h2 dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
<div
className="excerpt"
dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }}
/>
</article>
);
})}
</div>
);
}
export default PostList;
Обратите внимание на dangerouslySetInnerHTML — WordPress возвращает контент как HTML-строку, и это единственный способ корректно отрендерить теги форматирования, которые редакторы используют в Gutenberg.
Шаг 5: Работа с пользовательскими данными
Когда стандартных полей posts недостаточно, я подключаю плагин Advanced Custom Fields (ACF) с расширением ACF to REST API. Это добавляет кастомные поля в JSON-ответ:
export const getProductData = async (id) => {
const response = await axios.get(`${API_URL}/product/${id}?_fields=acf,title`);
return response.data;
};
Шаг 6: Аутентификация для форм и приватных данных
Если нужна отправка форм или доступ к защищённым эндпоинтам, использую JWT Authentication плагин:
export const authenticate = async (username, password) => {
const response = await axios.post(
'https://site.ru/wp-json/jwt-auth/v1/token',
{ username, password }
);
localStorage.setItem('token', response.data.token);
return response.data;
};
// Использование токена в запросах
const token = localStorage.getItem('token');
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
Подводные камни, на которые я наткнулся
Пагинация
WordPress по умолчанию отдаёт 10 постов. Для навигации использую заголовки ответа X-WP-TotalPages:
const response = await axios.get(`${API_URL}/posts?page=${page}&per_page=12`);
const totalPages = response.headers['x-wp-totalpages'];
Кэширование
На высоконагруженных проектах подключаю React Query — он избавляет от повторных запросов и делает UX плавнее:
npm install @tanstack/react-query
SEO
Это главный минус SPA — поисковики хуже индексируют динамический контент. Для решения использую React Helmet для метатегов или перехожу на Next.js с SSR, если SEO критично.
Итог
Связка React + WordPress REST API даёт лучшее из двух миров: привычный админ-интерфейс для контент-менеджеров и современный быстрый фронтенд для пользователей. За полгода использования этого стека на трёх проектах я убедился — это рабочее решение для блогов, интернет-магазинов и корпоративных сайтов, где важна скорость загрузки и гибкость интерфейса.