AgentSkillsCN

drupal-gutenberg-dev

为Drupal开发自定义Gutenberg区块与模块。适用于用户正在创建、修改或调试Drupal Gutenberg模块、自定义区块、JavaScript源文件、PHP集成代码、Twig模板,或为Drupal Gutenberg生态体系构建构建配置的场景。

SKILL.md
--- frontmatter
name: drupal-gutenberg-dev
version: 1.0.0
description: >
  Develop custom Gutenberg blocks and modules for Drupal. Use when the user is
  creating, modifying, or debugging Drupal Gutenberg modules, custom blocks,
  JavaScript source files, PHP integration code, Twig templates, or build
  configuration for the Drupal Gutenberg ecosystem.

Drupal Gutenberg development

Build custom Gutenberg blocks and modules for Drupal.

Version target: Drupal Gutenberg 3.x, which bundles Gutenberg 16.7 (27 Sep 2023) / WordPress 6.4. Compatible with Drupal 10 and 11.

Getting started

The quickest way to a custom block is copying the example_block module included with Gutenberg:

  1. Copy gutenberg/modules/example_block to modules/custom/my_block
  2. Rename all example_block.* files to my_block.*
  3. Rename package.example.json to package.json
  4. Update all example_block references in .gutenberg.yml, .info.yml, .libraries.yml, and package.json
  5. Run npm install
  6. Enable the module: drush en my_block
  7. Run npm run build to compile ES6 to JS
  8. Run npm start for watch mode during development

Development workflow

Follow these steps in order when building a block. Choose the workflow that matches your block type.

Client-side blocks (save HTML)

Use this when the block saves its own HTML output — no server-side rendering needed.

  1. Scaffold module structure — copy gutenberg/modules/example_block or create MODULE.info.yml, MODULE.libraries.yml, MODULE.gutenberg.yml, package.json, .babelrc, and src/ directory
  2. Configure .gutenberg.yml — add your editor library under libraries-edit:
  3. Configure .libraries.yml — declare the compiled JS with gutenberg/edit-node as a dependency
  4. Register block in .es6.js — implement registerBlockType() with edit() and save() functions; use useBlockProps() / useBlockProps.save()
  5. Buildnpm install && npm run build
  6. Enabledrush en MODULE && drush cr
  7. Verify — run through the verification checklist below

Dynamic blocks (server-rendered via Twig)

Use this when block output is rendered by PHP/Twig — the block saves no HTML (save() returns null).

  1. Scaffold module structure — same as client-side, plus create MODULE.module and templates/ directory
  2. Configure .gutenberg.yml — add editor library under libraries-edit: AND add the block under dynamic-blocks:
  3. Configure .libraries.yml — declare the compiled JS with gutenberg/edit-node as a dependency
  4. Register block in .es6.js — implement registerBlockType() with edit() function; save() returns null
  5. Choose editor preview approach:
    • ServerSideRender — if the block can render without page context (use const ServerSideRender = wp.serverSideRender;)
    • Static placeholder — if the block depends on runtime context (current node, route data)
  6. Register theme hook — in MODULE.module, implement hook_theme() with 'base hook' => 'gutenberg_block'
  7. Create Twig template — in templates/gutenberg-block--{namespace}--{block-name}.html.twig; wrap output in {% if is_visible %}
  8. Implement preprocess hook — block-specific hook_preprocess_gutenberg_block__MODULE__BLOCK() with data loading, is_visible guard, and cache tags
  9. Buildnpm install && npm run build
  10. Enabledrush en MODULE && drush cr
  11. Verify — run through the verification checklist below

Verification checklist

After implementing a block, verify each item before considering the task complete:

  • Block appears in the Gutenberg inserter
  • No JavaScript errors in browser console
  • Edit controls (InspectorControls, RichText, etc.) work correctly
  • Save and reload the node: no "Invalid block" warning
  • npm run build completes without errors
  • drush cr and hard refresh show latest changes
  • Client-side blocks: save() output produces valid HTML markup
  • Dynamic blocks: ServerSideRender preview renders (or placeholder displays)
  • Dynamic blocks: front-end renders correctly
  • Dynamic blocks: cache tags invalidate when referenced entities change

Troubleshooting

If something isn't working, see troubleshooting.md for symptom-based debugging organized by category (block not appearing, validation errors, SSR issues, build problems, media, translations, and more).

File types

FilePurpose
my_block.info.ymlStandard Drupal module info
my_block.libraries.ymlDeclares CSS/JS assets via Libraries API
my_block.gutenberg.ymlRegisters custom blocks, categories, patterns
src/*.es6.jsES6 source files (edit/save functions, block registration)
dist/*.jsCompiled JavaScript output
templates/*.html.twigTwig templates for server-side rendered blocks

Differences from WordPress

AspectWordPressDrupal
Content storagePost metaHTML in node__body.body_value
Block declarationregister_block_type() + block.json.gutenberg.yml + Twig
SSRrender_callbackTwig templates + hooks
Assetswp_enqueue_script/style()Libraries API (.libraries.yml)
Build@wordpress/scripts (webpack)Babel/drupal-js-build (.es6.js)
MediaNative media libraryDrupal file entities + optional media module

Installation

bash
composer require 'drupal/gutenberg:^3.0'
drush en gutenberg

Then enable Gutenberg for specific content types at /admin/config/content/gutenberg.

Detailed references

See these files in this skill's directory:

  • module-structure.md -- file structure, .info.yml, .libraries.yml, .gutenberg.yml
  • javascript-build.md -- ES6 to ES5 workflow, Babel, webpack, React/JSX
  • block-registration.md -- registering blocks, categories, variations, block styles
  • server-side-rendering.md -- Twig templates, hooks, dynamic blocks, ServerSideRender
  • media-integration.md -- file entities, media library, media blocks, upload handling
  • troubleshooting.md -- symptom-based debugging for common block development issues