Блоки vs шорткоды в WordPress: что выбрать и когда

Блоки vs шорткоды в WordPress: что выбрать и когда

Кратко: В WordPress есть два основных способа вывести на страницу свой элемент: блок (Gutenberg) и шорткод. Блок вставляется в редакторе визуально, хранится в контенте как HTML-комментарий вида <!-- wp:... --> и рендерится через PHP-функцию или JavaScript. Шорткод — это короткая конструкция в квадратных скобках, например [ds_button], которую WordPress при выводе записи заменяет на результат вашей PHP-функции. Блоки удобнее для контент-редакторов и для новых проектов, шорткоды проще в коде и незаменимы там, где нужен один и тот же вывод в теме, виджетах, письмах и виджетах. На практике оба инструмента не конкурируют, а дополняют друг друга: блок вполне может быть построен поверх шорткода. Ниже — понятная разница, таблица сравнения, готовые примеры на PHP и JS и разбор типичных ошибок новичка.

Чем блоки отличаются от шорткодов: базовое сравнение

Если объяснять в двух словах, блок и шорткод решают одну задачу — вставить на страницу что-то, чего нет в стандартном наборе редактора. Разница в том, как это делается. Шорткод появился ещё в старом редакторе и работает по принципу «текст — плейсхолдер — результат». Вы пишете в контенте [ds_button], а WordPress перед выводом записи находит этот текст и подменяет его на готовый HTML, который вернула привязанная PHP-функция.

Блоки, или редактор Gutenberg, устроены иначе. Элемент вставляется в контент как блок с собственным типом, а в базе хранится специальный HTML-комментарий — например, <!-- wp:paragraph -->. Такой комментарий содержит и тип блока, и его атрибуты, а сам редактор умеет показывать блок «как есть» прямо во время правки. Для контент-менеджера это означает живой предпросмотр: он видит кнопку, карточку или галерею ровно такой, какой она будет на сайте.

Ключевое практическое отличие — порог входа. Чтобы сделать свой шорткод, достаточно одной функции на PHP из нескольких строк. Чтобы сделать собственный блок, нужно зарегистрировать тип блока и написать интерфейс редактирования, а это уже JavaScript и понимание устройства редактора. Поэтому новички часто начинают именно с шорткодов, а к блокам переходят позже — когда появляется потребность дать редакторам удобный визуальный инструмент.

И тут важно сразу снять популярный миф: «шорткоды устарели, нужны только блоки». Это не так. Шорткоды официально поддерживаются ядром и будут работать ещё долго. Более того, многие популярные плагины — формы, слайдеры, галереи, конструкторы таблиц — до сих пор добавляют контент именно шорткодами, потому что так проще и надёжнее. Другой вопрос, что для собственной разметки в новом проекте блок почти всегда удобнее для конечного пользователя.

Как устроены блоки Gutenberg и где они хранятся

Блок — это не отдельная запись в базе, а размеченный фрагмент контента той же записи. Когда вы вставляете блок, WordPress дописывает в тело поста комментарии-обёртки. Для простого абзаца это выглядит так:

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">Текст абзаца.</p>
<!-- /wp:paragraph -->

Открывающий комментарий хранит имя блока и его атрибуты в формате JSON, а между открывающим и закрывающим комментарием лежит обычный HTML — результат функции save(). Такой подход удобен тем, что контент остаётся читаемым: если плагин с блоком отключат, пользователь увидит аккуратный HTML, а не «сырые» плейсхолдеры. У шорткода ситуация обратная: если плагин выключить, в тексте останется висеть строка вида [ds_button].

У блока есть три обязательные части. Первая — имя в формате namespace/block-name, например ds/callout. Вторая — функция редактирования edit() на JavaScript, которая рисует блок в редакторе. Третья — функция save(), которая сохраняет разметку в контент. Если блок нужно рендерить на сервере в момент показа страницы (например, выводить свежие данные), вместо save() указывают render_callback на PHP — так делают динамические блоки.

Внутри редактора блок можно настраивать через панель справа: атрибуты из block.json автоматически превращаются в поля InspectorControls — цвет, выравнивание, выбор категории и так далее. Именно это делает блоки такими удобными для контент-редакторов: им не нужно знать ни HTML, ни шорткод, они просто нажимают кнопки. Подробный разбор того, как создать блок с нуля и собрать его через @wordpress/scripts, есть в отдельном материале про свои блоки Gutenberg.

Ещё один плюс блоков — шаблоны и патерны. Один раз собрав удачную секцию из блоков, её можно сохранить как переиспользуемый шаблон и вставлять на любую страницу в один клик. Со шорткодами такого визуального механизма нет: приходится копировать текст с параметрами вручную.

Как работают шорткоды WordPress и зачем они ещё нужны

Шорткод — это, по сути, официальный механизм подстановки. Когда WordPress готовит контент записи к выводу, он прогоняет его через функцию do_shortcode(), находит в тексте зарегистрированные конструкции в квадратных скобках и заменяет их результатом соответствующих функций. Регистрируется шорткод одной строкой: add_shortcode( 'имя', 'функция_обработчик' ).

Главное преимущество шорткода — он работает везде, где прогоняется контент. Это не только основное поле записи, но и виджеты с текстом, описания товаров, письма, генерируемые на PHP, а также любые места темы, где разработчик вызвал do_shortcode() вручную. Блок такой универсальностью не обладает: его нельзя так же легко вывести внутри письма или в шаблоне без вызова do_blocks().

Второе преимущество — низкий порог входа. Шорткод не требует сборки, npm и React. Достаточно дописать функцию в functions.php дочерней темы или в собственный плагин, и он сразу доступен. Для задач «вывести телефон из настроек», «вставить кнопку с одинаковым оформлением», «показать список последних статей» этого более чем достаточно.

Третье — предсказуемость в шаблонах. Если один и тот же вывод нужен и в записи, и в футере, и в письме, удобнее иметь одну PHP-функцию и вызывать её напрямую, а в контенте использовать короткую обёртку-шорткод. Так не приходится дублировать разметку.

Есть у шорткодов и слабые места. Главное — их не видно в редакторе: контент-менеджер видит только строку [ds_button url="..."] и должен помнить синтаксис и названия параметров. Ошибка в кавычке или в имени атрибута приводит к тому, что на сайте выводится пустота или «сырой» текст. Ещё один минус — сложные вложенные шорткоды быстро превращаются в нечитаемую кашу. Именно из-за этого появились блоки. Базовую механику шорткодов мы разбирали в статье про создание собственных шорткодов в WordPress.

Блоки или шорткоды: сравнительная таблица

Чтобы выбрать осознанно, удобно разложить оба инструмента по одним и тем же критериям. Вот сводное сравнение.

Критерий Блоки (Gutenberg) Шорткоды
Как выглядит в контенте HTML-комментарий <!-- wp:... --> Текст вида [my_shortcode]
Кто редактирует Контент-менеджер, визуально Тот, кто знает синтаксис и атрибуты
Предпросмотр в редакторе Точный, «что вижу — то и на сайте» Нет, результат виден только на фронте
Порог входа в разработку Нужен JavaScript и понимание редактора Достаточно одной PHP-функции
Настройки Панель свойств справа от контента Атрибуты прямо в тексте [x foo="bar"]
Где можно вставить Записи и страницы, виджеты-блоки Контент, виджеты, темы, письма
Повторное использование Шаблоны блоков и патерны Копирование строки шорткода
Что останется при отключении Читаемый HTML-код Текст со скобками [my_shortcode]
Гибкость разметки Ограничена логикой блока Полный контроль внутри PHP

Из таблицы видно закономерность: чем важнее удобство для редактора, тем сильнее аргументы в пользу блока. Чем важнее простота кода и универсальность вывода, тем выгоднее шорткод. Это не «или-или», а вопрос приоритета: для кого мы оптимизируем — для контент-менеджера или для разработчика.

Что выбрать в конкретных ситуациях: 6 сценариев

Абстрактные сравнения редко помогают в реальной задаче, поэтому разберём частые сценарии и однозначные рекомендации.

  • Новый сайт, редакторы — не программисты. Выбирайте блоки. Визуальная правка снимает 90% вопросов и ошибок с синтаксисом. Шорткоды оставьте для случаев, где блок избыточен.
  • Нужна кнопка или короткий элемент в тексте повсюду. Хватит шорткода. Одна функция, единое оформление, работает в записях и виджетах без лишнего кода.
  • Сложная карточка товара или услуги с полями. Лучше блок с атрибутами: редактор заполнит поля в панели, а не будет писать длинный шорткод с десятью параметрами.
  • Один и тот же вывод нужен и на сайте, и в письмах. Делайте функцию на PHP, а в контенте используйте тонкий шорткод-обёртку. Блок в письмо так просто не поставишь.
  • Вывод данных, которые меняются (курс, счётчик, свежие посты). Динамический блок с render_callback либо шорткод — оба рендерятся на сервере, выбирайте по удобству вставки.
  • Старый сайт на классическом редакторе. Шорткоды, без вариантов: блоки там просто не появятся без перехода на Gutenberg.

Практическое правило для новичка можно сформулировать так. Если элемент вставляется в текст записи или страницы и им будет пользоваться человек без технических знаний — делайте блок. Если элемент нужно вызывать из кода, из темы, из письма или он должен выглядеть одинаково в самых разных местах — делайте шорткод. И почти всегда не будет ошибкой сделать функцию на PHP, а поверх неё — и шорткод, и блок.

Как создать свой шорткод: рабочие примеры кода

Начнём с самого частого примера — кнопка со ссылкой и стилем. Регистрируем шорткод и обработчик, обязательно используем shortcode_atts() для значений по умолчанию и функции экранирования при выводе.

<?php
/**
 * Шорткод кнопки: [ds_button url="https://site.ru" style="primary"]Текст[/ds_button]
 */
add_shortcode( 'ds_button', 'ds_button_shortcode' );

function ds_button_shortcode( $atts, $content = null ) {
    $atts = shortcode_atts( array(
        'url'   => '#',
        'style' => 'primary',
    ), $atts, 'ds_button' );

    return sprintf(
        '<a class="ds-btn ds-btn--%s" href="%s">%s</a>',
        esc_attr( $atts['style'] ),
        esc_url( $atts['url'] ),
        esc_html( $content )
    );
}

Разберём принципиальные моменты. shortcode_atts() третий аргумент — имя шорткода — нужен, чтобы другие плагины могли отфильтровать параметры. esc_url() защищает адрес ссылки, esc_attr() — значение класса, esc_html() — текст между тегами. Если этого не сделать, злоумышленник с правом на публикацию сможет вставить в атрибут кавычку и добавить свой HTML. Общий подход к экранированию и другие полезные приёмы собраны в подборке про полезные сниппеты для functions.php.

Второй частый случай — шорткод, которому нужны собственные стили и скрипты. Подключать их глобально не стоит: это замедлит все страницы. Правильнее подключать файлы только тогда, когда шорткод действительно есть в контенте. Сделать это можно прямо внутри обработчика через wp_enqueue_style() и wp_enqueue_script().

<?php
/**
 * Шорткод галереи: [ds_gallery ids="12,15,18"]
 */
add_shortcode( 'ds_gallery', 'ds_gallery_shortcode' );

function ds_gallery_shortcode( $atts ) {
    $atts = shortcode_atts( array( 'ids' => '' ), $atts, 'ds_gallery' );

    // Стили подключаем только там, где шорткод реально есть на странице.
    wp_enqueue_style(
        'ds-gallery',
        get_stylesheet_directory_uri() . '/css/gallery.css',
        array(),
        '1.0'
    );

    $ids = array_filter( array_map( 'absint', explode( ',', $atts['ids'] ) ) );
    if ( empty( $ids ) ) {
        return '';
    }

    $out = '<div class="ds-gallery">';
    foreach ( $ids as $id ) {
        $out .= wp_get_attachment_image( $id, 'medium', false, array( 'loading' => 'lazy' ) );
    }
    $out .= '</div>';

    return $out;
}

Обратите внимание: список ID очищается через array_map( 'absint', ... ), поэтому в функцию не попадёт ничего, кроме положительных чисел. Картинки выводятся стандартной wp_get_attachment_image(), которая сама добавляет адаптивные атрибуты и ленивую загрузку. Больше о сжатии картинок и формате WebP — в материале про оптимизацию изображений в WordPress.

Иногда шорткоду нужно вернуть сложную разметку из нескольких условий. Чтобы не склеивать строки «плюсами» и не путаться в кавычках, удобно собирать вывод в буфер и вернуть его одним куском:

<?php
/**
 * Шорткод с буферизацией вывода: удобно для длинной разметки.
 */
add_shortcode( 'ds_card', 'ds_card_shortcode' );

function ds_card_shortcode( $atts, $content = null ) {
    $atts = shortcode_atts( array(
        'title' => '',
        'link'  => '',
    ), $atts, 'ds_card' );

    ob_start(); // начинаем перехват вывода.
    ?>
    <div class="ds-card">
        <h3 class="ds-card__title"><?php echo esc_html( $atts['title'] ); ?></h3>
        <div class="ds-card__body"><?php echo wp_kses_post( $content ); ?></div>
        <?php if ( $atts['link'] ) : ?>
            <a class="ds-card__link" href="<?php echo esc_url( $atts['link'] ); ?>">Подробнее</a>
        <?php endif; ?>
    </div>
    <?php
    return ob_get_clean(); // отдаём накопленное как результат шорткода.
}

Приём с ob_start() и ob_get_clean() особенно любят те, кто пишет много разметки: вместо конкатенации строк код остаётся обычным HTML с вставками PHP. Если шорткод должен содержать внутри другой шорткод, не забудьте вызвать do_shortcode( $content ) перед выводом — иначе вложенный шорткод так и останется текстом.

Как создать свой блок Gutenberg: пример с PHP и JS

Теперь то же самое блоком. Есть два способа: полностью на JavaScript со сборкой и с динамическим рендером на PHP. Покажем оба, начав с более простого — динамического блока без сборки, с render_callback. В этом случае разметку формирует PHP в момент показа страницы, а в контенте сохраняется только вызов блока с атрибутами.

<?php
/**
 * Регистрируем динамический блок ds/callout.
 */
add_action( 'init', 'ds_register_blocks' );

function ds_register_blocks() {
    register_block_type( 'ds/callout', array(
        'render_callback' => 'ds_render_callout',
        'attributes'      => array(
            'title' => array( 'type' => 'string', 'default' => '' ),
            'text'  => array( 'type' => 'string', 'default' => '' ),
        ),
    ) );
}

function ds_render_callout( $attributes ) {
    return sprintf(
        '<div class="ds-callout"><strong>%s</strong><p>%s</p></div>',
        esc_html( $attributes['title'] ),
        wp_kses_post( $attributes['text'] )
    );
}

Регистрация вешается на хук init — это стандартное место для блоков, типов записей и таксономий. Атрибуты описываются как обычный массив, а render_callback получает их на вход и возвращает готовый HTML. Такой блок удобен, когда содержимое зависит от данных на сервере: например, нужно всегда показывать актуальное число товаров или последние записи.

Если блок должен редактироваться визуально в самом редакторе, без сборки, его можно зарегистрировать на JavaScript, подключив зависимости wp.blocks, wp.element и wp.blockEditor. Такой подход годится для простых блоков, которые не хочется собирать через инструменты вроде @wordpress/scripts.

// JS: регистрируем простой блок ds/note без сборки.
( function ( blocks, element, blockEditor, i18n ) {
    var el = element.createElement;
    var __ = i18n.__;
    var useBlockProps = blockEditor.useBlockProps;
    var RichText = blockEditor.RichText;

    blocks.registerBlockType( 'ds/note', {
        title: __( 'Заметка', 'ds' ),
        icon: 'info-outline',
        category: 'text',
        attributes: {
            content: { type: 'string', source: 'html', selector: 'p' }
        },
        edit: function ( props ) {
            return el(
                'div',
                useBlockProps( { className: 'ds-note' } ),
                el( RichText, {
                    tagName: 'p',
                    value: props.attributes.content,
                    onChange: function ( value ) {
                        props.setAttributes( { content: value } );
                    },
                    placeholder: __( 'Текст заметки…', 'ds' )
                } )
            );
        },
        save: function ( props ) {
            return el( 'p', useBlockProps.save(), props.attributes.content );
        }
    } );
} )( wp.blocks, wp.element, wp.blockEditor, wp.i18n );

Здесь registerBlockType() получает имя блока, атрибуты, функцию edit() для редактора и save() для сохранения разметки в контент. Компонент RichText даёт редактируемое текстовое поле, а useBlockProps() добавляет обязательные служебные классы, без которых блок перестанет корректно отображаться. Скрипт подключается на фронте и в редакторе через wp_enqueue_script() с зависимостями — как это делается, подробно описано в статье про подключение CSS и JS через wp_enqueue_scripts.

Для серьёзных проектов правильнее собирать блок через block.json и @wordpress/scripts: тогда есть автодополнение, проверка ошибок и единая сборка стилей и скриптов. Пошаговая инструкция со структурой файлов есть в гайде про создание своего блока Gutenberg.

Как использовать блоки и шорткоды вместе

Чаще всего выбор «или-или» вообще не стоит: грамотный проект использует оба инструмента. Самый распространённый гибридный приём — обернуть шорткод в блок. Это даёт двойную выгоду: логика остаётся в простой PHP-функции, а редактор получает визуальный элемент с настройками.

<?php
/**
 * Блок-обёртка, который внутри выводит наш шорткод.
 * Так редактор видит блок, а код остаётся в одном месте.
 */
add_action( 'init', 'ds_register_button_block' );

function ds_register_button_block() {
    register_block_type( 'ds/button', array(
        'render_callback' => 'ds_render_button_block',
        'attributes'      => array(
            'url'   => array( 'type' => 'string', 'default' => '' ),
            'label' => array( 'type' => 'string', 'default' => 'Подробнее' ),
        ),
    ) );
}

function ds_render_button_block( $attributes ) {
    // Переиспользуем уже существующую логику шорткода — не дублируем разметку.
    return do_shortcode( sprintf(
        '[ds_button url="%s"]%s[/ds_button]',
        esc_attr( $attributes['url'] ),
        esc_html( $attributes['label'] )
    ) );
}

Обратный приём тоже бывает полезен: вывести блок или его результат там, где доступен только PHP. Для этого есть функция do_blocks(), которая разбирает строку с блоками и возвращает готовый HTML. Например, если вы храните готовый шаблон в опции и хотите показать его в теме:

<?php
// Выводим готовый шаблон блоков из опции настройки.
$template = get_option( 'ds_landing_template', '' );
echo do_blocks( $template );

А внутри save() и шаблонов блоков работает и обратный вызов — do_shortcode(), которым можно разрешить вложенные шорткоды в атрибутах блока. Такая связка «PHP-функция → шорткод → блок» считается хорошей практикой: вся логика в одном месте, а точки входа разные. Если вы используете для контента произвольные типы записей и таксономии, тот же принцип удобно переносится и на них — об этом есть отдельный разбор про кастомные типы записей. А если шорткод показывает кастомные поля, пригодится статья про поля ACF в WordPress.

Ещё один сценарий — плавная миграция. Если сайт годами использовал шорткоды, не обязательно всё переписывать. Можно оставить шорткоды как есть, а новые элементы делать блоками, постепенно переводя старые. Если шорткод генерирует объёмную разметку, имеет смысл однажды заменить его вызов в контенте на блок, а саму PHP-функцию сохранить для писем и шаблонов.

Частые ошибки при работе с блоками и шорткодами

Большинство проблем у новичков типовые. Ниже — список, который стоит проверить прежде, чем искать баг глубже.

  • Шорткод выводится как текст. Чаще всего функция не зарегистрирована или зарегистрирована после того, как контент уже отрендерен. Проверьте, что add_shortcode() вызывается на этапе загрузки, а не внутри другого хука с поздним срабатыванием.
  • Шорткод не работает в виджете или письме. Для виджетов текстовый контент часто не прогоняется через do_shortcode(). Добавьте фильтр или вызывайте функцию напрямую. В письмах шорткоды тоже не обрабатываются автоматически.
  • Блок не появляется в редакторе. Скрипты регистрации должны подключаться и на фронте, и в редакторе, а register_block_type() — срабатывать на init. Проверьте зависимости скрипта: без wp-blocks и wp-element блок не зарегистрируется.
  • Блок «сломался» после обновления. Если менялась разметка save(), WordPress сообщит о несоответствии. Для меняющейся разметки используйте динамический блок с render_callback, а не save().
  • Стили блока видны и на фронте, и в редакторе не так. Разные файлы отвечают за разные контексты: style — для фронта, editor_style — для редактора. Подключайте оба, иначе блок будет выглядеть по-разному.
  • В шорткоде есть XSS. Значения из атрибутов выводятся без экранирования. Используйте esc_html(), esc_attr() и esc_url() всегда, даже если данные кажутся безобидными.
  • Правки пропадают при обновлении темы. Код был в родительской теме. Переносите блоки и шорткоды в дочернюю тему или в отдельный плагин — как это устроить, рассказано в гайде про дочернюю тему WordPress.

Отдельно стоит помнить про производительность. Каждый шорткод и каждый динамический блок — это выполнение PHP при каждом показе страницы. Если элемент тяжёлый, его результат имеет смысл кэшировать через транзиенты или общий кэш страниц. Как это устроено, разобрано в статье про кэширование WordPress. А если вы оборачиваете вывод в свой шаблон, полезно понимать механику шаблонов — о ней есть материал про кастомные шаблоны страниц. Наконец, если элемент должен отдавать данные наружу — например, в мобильное приложение — стоит взглянуть на REST API WordPress. И помните: блоки и шорткоды — лишь надстройка над хуками actions и filters, поэтому их фундамент стоит понять в первую очередь.

TimeWeb

Частые вопросы о блоках и шорткодах WordPress

Что лучше — блоки или шорткоды в WordPress?

Однозначного победителя нет. Блоки удобнее для контент-редакторов: элемент вставляется визуально и настраивается в панели свойств. Шорткоды проще в разработке — достаточно одной PHP-функции — и работают там, где блоки недоступны: в виджетах, темах и письмах. Для нового сайта с непрограммирующими редакторами выбирайте блоки, для быстрых вставок и вывода из кода — шорткоды.

Устарели ли шорткоды и будут ли их поддерживать?

Нет, шорткоды официально поддерживаются ядром WordPress и никуда не исчезнут в обозримом будущем. Множество активных плагинов — формы, слайдеры, галереи — добавляют контент именно шорткодами. Блоки появились как более удобная альтернатива для редакторов, но не как замена шорткодам.

Как создать свой шорткод в WordPress?

Зарегистрируйте его функцией add_shortcode() и передайте имя обработчика — свою PHP-функцию. Внутри используйте shortcode_atts() для значений по умолчанию и функции экранирования при выводе. Пример: add_shortcode( 'ds_button', 'ds_button_shortcode' ), затем функция, которая возвращает готовый HTML. Вызов в контенте — [ds_button].

Как создать свой блок Gutenberg с примерами кода?

Самый простой путь — динамический блок через register_block_type() на хуке init с параметром render_callback: разметку формирует PHP-функция, а в контенте хранится только вызов блока с атрибутами. Для визуального редактирования блок регистрируют на JavaScript через registerBlockType() с функциями edit() и save(). В крупных проектах блок собирают через block.json и @wordpress/scripts.

Почему шорткод выводится как обычный текст?

Обычно потому, что функция не зарегистрирована или контент не прошёл через do_shortcode(). В виджетах и письмах автообработки может не быть — добавьте фильтр или вызовите функцию напрямую. Вторая причина: опечатка в имени шорткода в контенте или регистрация происходит позже, чем рендерится запись.

Можно ли использовать блок и шорткод вместе?

Да, и это часто лучший вариант. Можно зарегистрировать блок с render_callback, который внутри вызывает do_shortcode(), — тогда логика остаётся в одной PHP-функции, а редактор видит удобный блок с настройками. Обратно, результат блоков из шаблона можно вывести функцией do_blocks(). Так код не дублируется, а контент-редактор работает визуально.

Вывод

Выбор между блоками и шорткодами — это не спор о том, что современнее, а вопрос о том, для кого и где вы делаете элемент. Блок незаменим, когда с контентом работает редактор без технических знаний: визуальная правка, панель настроек и живой предпросмотр экономят часы. Шорткод незаменим в коде: одна PHP-функция, работа в виджетах и письмах, полный контроль над разметкой и минимум зависимостей.

Главные мысли материала можно свести к трём пунктам. Первое: блоки и шорткоды не конкурируют, а дополняют друг друга, и лучшее решение часто гибридное — логика в PHP, обёртка в блоке. Второе: любой вывод из формы или атрибутов всегда экранируйте — это правило одинаково для обоих инструментов. Третье: сложные элементы, которые меняются со временем, делайте динамическими через render_callback, а не через статичный save().

Если разбираться с блоками, шорткодами и доработками сайта некогда, это можно поручить команде, которая делает сайты на WordPress под ключ: мы собираем свои блоки и элементы, настраиваем редактор под редакторов и сопровождаем проект после запуска. Посмотрите услуги разработки и продвижения, изучите портфолио выполненных проектов и почитайте блог о сайтах и продвижении. Обсудить задачу и получить смету можно через форму контактов — подберём решение под ваш проект и бюджет.

Clearfy

Поделиться:
Telegram ВКонтакте