Шорткод или Gutenberg-блок: три подхода и цена каждого
Карточку цены можно сделать шорткодом, статическим или динамическим блоком, и через год разница проявится на каждой правке вёрстки. Разбираем, что хранится в базе и чем вы платите при смене разметки в WordPress 6.x.

Заказчик просит карточку с ценой и кнопкой на двадцати страницах. Первая мысль: шорткод, десять минут работы. Через год дизайнер меняет вёрстку, и выясняется, что в записях лежит не намерение, а готовый HTML, причём в трёх разных форматах. Выбор между шорткодом, статическим и динамическим блоком в WordPress 6.x решает, что именно хранится в записи и сколько стоит каждая следующая правка. Ниже три подхода, код по делу и критерий выбора.
Что хранится в базе
Подходы отличаются одним: что попадает в post_content.
- Шорткод: строка вида
[price_card title="Базовый" price="1990"]. Вёрстка живёт в PHP. - Статический блок: готовый HTML внутри комментариев-разделителей. Вёрстка лежит и в записи, и в JS-функции
save. - Динамический блок: только комментарий с атрибутами. Вёрстка живёт в PHP.
Пояснение к термину. Gutenberg сохраняет блоки в обычный HTML и размечает их комментариями вида «wp:имя-блока», а атрибуты кладёт туда же в виде JSON. Это и называют блочной разметкой. Редактор при загрузке сверяет её с тем, что выдаёт текущая функция save.
Три подхода и цена каждого
Шорткод: быстро и без сборки
Нужен только PHP, Node не требуется. Три правила, на которых обычно ломаются: возвращайте строку, а не печатайте её; задавайте значения по умолчанию через shortcode_atts; экранируйте вывод. Атрибуты вводят все, кто правит записи, и без экранирования это готовая дыра для XSS.
<?php
// WordPress 6.x, PHP 8.x. Использование: [price_card title="Базовый" price="1990"]
add_shortcode( 'price_card', 'myplugin_price_card' );
function myplugin_price_card( $atts ) {
// Неизвестные атрибуты отбрасываются, пропущенные получают значения по умолчанию
$atts = shortcode_atts(
array(
'title' => '',
'price' => 0,
),
$atts,
'price_card'
);
// Возвращаем строку: echo вывел бы разметку выше текста записи
return sprintf(
'<div class="price-card"><h3>%s</h3><p>%s ₽</p></div>',
esc_html( $atts['title'] ),
esc_html( number_format_i18n( (float) $atts['price'] ) )
);
}Цена: в блочном редакторе шорткод виден как текст в скобках, а все атрибуты приходят строками. Опечатка в имени атрибута не вызывает ошибки, значение просто молча пропадает.
Статический блок: хранит результат
Функция save на JS строит HTML, и он записывается в запись. В редакторе блок выглядит как на сайте, а при показе PHP не выполняется. Платите вы при первой же правке разметки: старые записи перестают проходить проверку, и редактор пишет «Block contains unexpected or invalid content». Лечится массивом deprecated, в котором лежит старая версия save. Каждая правка вёрстки добавляет туда новый элемент.
// схематично: прежняя версия save остаётся в deprecated
registerBlockType( 'myplugin/price-card', {
// title, attributes, edit опущены
save: SaveV2,
deprecated: [
{ attributes: oldAttributes, save: SaveV1 },
],
} );Динамический блок: хранит намерение
В записи остаётся только комментарий с атрибутами, а разметку строит PHP при каждом показе. Поле render в block.json появилось в WordPress 6.1, apiVersion 3 рассчитан на 6.3 и новее. Для более старых версий используйте аргумент render_callback у register_block_type. Каркас с редакторской частью даёт команда npx @wordpress/create-block@latest price-card, про флаги смотрите документацию пакета. В JS у такого блока save возвращает null.
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "myplugin/price-card",
"title": "Карточка цены",
"category": "widgets",
"attributes": {
"title": { "type": "string", "default": "" },
"price": { "type": "number", "default": 0 }
},
"editorScript": "file:./index.js",
"render": "file:./render.php"
}<?php
// render.php. Здесь доступны $attributes, $content и $block
$title = isset( $attributes['title'] ) ? $attributes['title'] : '';
$price = isset( $attributes['price'] ) ? (float) $attributes['price'] : 0;
?>
<div <?php echo get_block_wrapper_attributes(); ?>>
<h3><?php echo esc_html( $title ); ?></h3>
<p><?php echo esc_html( number_format_i18n( $price ) ); ?> ₽</p>
</div>Регистрация в плагине для сборки create-block: register_block_type( __DIR__ . '/build' ) на хуке init. Цена: нужен Node и этап сборки, а PHP выполняется при каждой отрисовке контента.
Сравнение
| Критерий | Шорткод | Статический блок | Динамический блок |
|---|---|---|---|
| Что в записи | Строка с атрибутами | Готовый HTML | Комментарий с атрибутами |
| Правка вёрстки | Меняете PHP, обновляются все записи | Нужен deprecated, старые записи сами не обновятся | Меняете PHP, обновляются все записи |
| Вид в редакторе | Текст в скобках | Как на сайте | Как на сайте, если написан edit |
| Сборка на Node | Не нужна | Нужна | Нужна для редакторской части |
| PHP при показе | Выполняется | Не выполняется | Выполняется |
Когда что выбирать
- Шорткод: вставка нужна в старых записях, виджетах или классическом редакторе, в проекте нет Node, атрибутов два-три, контент правят не визуально.
- Статический блок: вёрстка почти не меняется, данных из PHP нет, нужен точный вид в редакторе.
- Динамический блок: данные приходят из PHP (мета-поля, список записей) или вёрстку будут менять.
Рабочий критерий: если содержимое должно обновиться без правки самой записи, статический блок вам не подходит. Остаются шорткод и динамический блок. Если нужен ещё и визуальный редактор, берите динамический. Паттерны и Block Bindings здесь не разбираем, это отдельные темы.
Шорткод и динамический блок хранят намерение, статический блок хранит результат. Результат устаревает, намерение можно перевычислить.
Что ломается
- Шорткод печатает через
echo. Разметка вылезает над текстом записи, потому что вывод происходит раньше, чем контент собран. Всегдаreturn. - Подключение стилей. Для шорткода обычно проверяют
has_shortcode( $post->post_content, 'price_card' )на хукеwp_enqueue_scripts. Такая проверка не увидит шорткод из виджета или произвольного поля. У блока полеstyleвblock.jsonподключается только на страницах, где блок есть. - Переименованный атрибут блока. Старые значения не переедут сами: нужен
deprecatedили миграция. - Деактивированный плагин. Шорткод на сайте остаётся голым текстом в скобках, динамический блок молча исчезает, статический остаётся обычным HTML.
- Шорткод внутри содержимого блока. Если нужен вложенный шорткод, прогоняйте текст через
do_shortcode. Шорткод в классическом редакторе проверяйте отдельно, автоматические абзацы могут обернуть его в лишний<p>.
Что делать дальше
- Деактивируйте плагин на копии сайта и посмотрите, как выглядят записи без него.
- Измерьте время генерации страницы с блоком и без него, например плагином Query Monitor. Если карточка ходит во внешний API, кешируйте ответ через transients.
- Для миграции со старых шорткодов изучите трансформации блока с типом
shortcodeв документации редактора. - Актуальный список функций и параметров шорткодов смотрите в руководстве для разработчиков плагинов.
Карточки для маркетплейсов: Таблица товаров и папка фотографий на входе — комплект слайдов и описаний на выходе, по требованиям Wildberries и Ozon.
Спросить за 5 ₽Источники: WordPress Developer Resources: Shortcodes, WordPress Developer Resources: Block Editor Handbook
Частые вопросы
Нужно ли переписывать старые шорткоды на блоки?
А если у меня другая версия WordPress?
Блок не появился в редакторе. С чего начать?
Что учесть при запуске в прод?
Можно ли вставить блок в классический редактор?
Безопасно ли выводить атрибуты как есть?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.