Розробка кастомних блоків Gutenberg для WordPress

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка кастомних блоків Gutenberg для WordPress
Середня
~3-5 робочих днів
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка користувацьких блоків Gutenberg для WordPress

Gutenberg замінив TinyMCE як основний редактор WordPress у версії 5.0. Користувацькі блоки — це React-компоненти, які відображаються і в редакторі, і на фронтенді. Вони дають редакторам візуальний контроль над структурованим вмістом без необхідності знати HTML. Розробка одного блока середної складності займає 2–4 дні.

Реєстрація блока

Сучасний підхід — block.json + JavaScript/PHP:

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "my-plugin/project-card",
  "version": "1.0.0",
  "title": "Карточка проекту",
  "category": "common",
  "icon": "portfolio",
  "description": "Виводить карточку портфоліо-проекту з зображенням і описом",
  "supports": {
    "html": false,
    "align": ["wide", "full"],
    "color": { "background": true, "text": true }
  },
  "attributes": {
    "projectId": { "type": "number" },
    "showDescription": { "type": "boolean", "default": true },
    "imageSize": { "type": "string", "default": "large" }
  },
  "editorScript": "file:./index.js",
  "editorStyle": "file:./editor.css",
  "style": "file:./style.css"
}

Реєстрація в PHP:

add_action('init', function () {
    register_block_type(__DIR__ . '/blocks/project-card');
});

JavaScript: edit і save

import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, InspectorControls, MediaUpload } from '@wordpress/block-editor';
import { PanelBody, ToggleControl, SelectControl, Button } from '@wordpress/components';
import { useSelect } from '@wordpress/data';

registerBlockType('my-plugin/project-card', {
  edit: ({ attributes, setAttributes }) => {
    const { projectId, showDescription, imageSize } = attributes;
    const blockProps = useBlockProps({ className: 'project-card-editor' });

    const projects = useSelect(select =>
      select('core').getEntityRecords('postType', 'project', { per_page: 50 })
    );

    const projectOptions = projects
      ? [{ label: '— виберіть проект —', value: 0 }, ...projects.map(p => ({ label: p.title.rendered, value: p.id }))]
      : [{ label: 'Завантаження...', value: 0 }];

    return (
      <>
        <InspectorControls>
          <PanelBody title="Налаштування блока">
            <SelectControl
              label="Проект"
              value={projectId}
              options={projectOptions}
              onChange={v => setAttributes({ projectId: Number(v) })}
            />
            <ToggleControl
              label="Показувати опис"
              checked={showDescription}
              onChange={v => setAttributes({ showDescription: v })}
            />
            <SelectControl
              label="Розмір зображення"
              value={imageSize}
              options={[
                { label: 'Thumbnail', value: 'thumbnail' },
                { label: 'Medium', value: 'medium' },
                { label: 'Large', value: 'large' },
              ]}
              onChange={v => setAttributes({ imageSize: v })}
            />
          </PanelBody>
        </InspectorControls>
        <div {...blockProps}>
          {projectId
            ? <ProjectCardPreview projectId={projectId} showDescription={showDescription} />
            : <p>Виберіть проект у панелі справа</p>
          }
        </div>
      </>
    );
  },

  save: () => null, // динамічний блок — рендер через PHP
});

save: () => null означає, що блок динамічний — контент рендерується PHP в момент запиту сторінки. Це переважно для блоків, дані яких змінюються (записи з БД).

PHP-рендеринг динамічного блока

register_block_type(__DIR__ . '/blocks/project-card', [
    'render_callback' => 'my_plugin_render_project_card',
]);

function my_plugin_render_project_card(array $attributes): string {
    $project_id      = absint($attributes['projectId'] ?? 0);
    $show_desc       = (bool) ($attributes['showDescription'] ?? true);
    $image_size      = sanitize_key($attributes['imageSize'] ?? 'large');

    if (!$project_id) return '';

    $project = get_post($project_id);
    if (!$project || $project->post_status !== 'publish') return '';

    $thumbnail = get_the_post_thumbnail($project_id, $image_size, ['class' => 'project-card__image']);
    $title     = esc_html($project->post_title);
    $permalink = esc_url(get_permalink($project_id));
    $excerpt   = $show_desc ? '<p class="project-card__desc">' . esc_html(get_the_excerpt($project)) . '</p>' : '';

    $wrapper_attributes = get_block_wrapper_attributes(['class' => 'project-card']);

    return "<article {$wrapper_attributes}>
        {$thumbnail}
        <h3 class=\"project-card__title\"><a href=\"{$permalink}\">{$title}</a></h3>
        {$excerpt}
    </article>";
}

get_block_wrapper_attributes() додає класи з supports.color та інші атрибути, які Gutenberg генерує автоматично.

Блок з innerBlocks

Блоки-контейнери приймають дочірні блоки через InnerBlocks:

import { InnerBlocks } from '@wordpress/block-editor';

const ALLOWED_BLOCKS = ['core/paragraph', 'core/heading', 'my-plugin/cta-button'];
const TEMPLATE = [
  ['core/heading', { level: 3, placeholder: 'Заголовок секції' }],
  ['core/paragraph', { placeholder: 'Опис...' }],
  ['my-plugin/cta-button', {}],
];

// У edit:
<InnerBlocks allowedBlocks={ALLOWED_BLOCKS} template={TEMPLATE} templateLock={false} />

// У save:
<InnerBlocks.Content />

Збірка

Блоки збираються через @wordpress/scripts:

{
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start",
    "lint:js": "wp-scripts lint-js"
  },
  "devDependencies": {
    "@wordpress/scripts": "^27.0.0"
  }
}

wp-scripts налаштований під WordPress за умовчанням: знає про @wordpress/* як про зовнішні залежності, генерує asset.php з хешем версії для коректної інвалідації кеша.

Розширення існуючих блоків через фільтри

Не завжди потребується новий блок — іноді достатньо додати атрибут або панель до існуючого:

import { addFilter } from '@wordpress/hooks';
import { createHigherOrderComponent } from '@wordpress/compose';

// Додаємо атрибут "data-section" до будь-якого блока
addFilter('blocks.registerBlockType', 'my-plugin/add-section-id', (settings) => {
  settings.attributes = {
    ...settings.attributes,
    sectionId: { type: 'string', default: '' },
  };
  return settings;
});

// Додаємо поле в InspectorControls
const withSectionIdControl = createHigherOrderComponent(BlockEdit => {
  return (props) => {
    const { attributes, setAttributes } = props;
    return (
      <>
        <BlockEdit {...props} />
        <InspectorControls>
          <PanelBody title="Якорна ссилка">
            <TextControl
              label="ID секції"
              value={attributes.sectionId}
              onChange={v => setAttributes({ sectionId: v })}
            />
          </PanelBody>
        </InspectorControls>
      </>
    );
  };
}, 'withSectionIdControl');

addFilter('editor.BlockEdit', 'my-plugin/section-id-control', withSectionIdControl);

Типові строки

Простий статичний блок з 2–3 атрибутами — 4–8 годин. Динамічний блок з PHP-рендером і панеллю налаштувань — 1–2 дні. Блок-контейнер з innerBlocks, власним стилем і server-side рендером — 2–4 дні. Набір з 5–10 пов'язаних блоків для дизайн-системи — від 2 тижнів.