From drupal-gutenberg
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.
How this skill is triggered — by the user, by Claude, or both
Slash command
/drupal-gutenberg:drupal-gutenberg-devThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Build custom Gutenberg blocks and modules for Drupal.
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.
The quickest way to a custom block is copying the example_block module included with Gutenberg:
gutenberg/modules/example_block to modules/custom/my_blockexample_block.* files to my_block.*package.example.json to package.jsonexample_block references in .gutenberg.yml, .info.yml, .libraries.yml, and package.jsonnpm installdrush en my_blocknpm run build to compile ES6 to JSnpm start for watch mode during developmentFollow these steps in order when building a block. Choose the workflow that matches your block type.
Use this when the block saves its own HTML output — no server-side rendering needed.
gutenberg/modules/example_block or create MODULE.info.yml, MODULE.libraries.yml, MODULE.gutenberg.yml, package.json, .babelrc, and src/ directory.gutenberg.yml — add your editor library under libraries-edit:.libraries.yml — declare the compiled JS with gutenberg/edit-node as a dependency.es6.js — implement registerBlockType() with edit() and save() functions; use useBlockProps() / useBlockProps.save()npm install && npm run builddrush en MODULE && drush crUse this when block output is rendered by PHP/Twig — the block saves no HTML (save() returns null).
MODULE.module and templates/ directory.gutenberg.yml — add editor library under libraries-edit: AND add the block under dynamic-blocks:.libraries.yml — declare the compiled JS with gutenberg/edit-node as a dependency.es6.js — implement registerBlockType() with edit() function; save() returns nullServerSideRender — if the block can render without page context (use const ServerSideRender = wp.serverSideRender;)MODULE.module, implement hook_theme() with 'base hook' => 'gutenberg_block'templates/gutenberg-block--{namespace}--{block-name}.html.twig; wrap output in {% if is_visible %}hook_preprocess_gutenberg_block__MODULE__BLOCK() with data loading, is_visible guard, and cache tagsnpm install && npm run builddrush en MODULE && drush crAfter implementing a block, verify each item before considering the task complete:
npm run build completes without errorsdrush cr and hard refresh show latest changessave() output produces valid HTML markupServerSideRender preview renders (or placeholder displays)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 | Purpose |
|---|---|
my_block.info.yml | Standard Drupal module info |
my_block.libraries.yml | Declares CSS/JS assets via Libraries API |
my_block.gutenberg.yml | Registers custom blocks, categories, patterns |
src/*.es6.js | ES6 source files (edit/save functions, block registration) |
dist/*.js | Compiled JavaScript output |
templates/*.html.twig | Twig templates for server-side rendered blocks |
| Aspect | WordPress | Drupal |
|---|---|---|
| Content storage | Post meta | HTML in node__body.body_value |
| Block declaration | register_block_type() + block.json | .gutenberg.yml + Twig |
| SSR | render_callback | Twig templates + hooks |
| Assets | wp_enqueue_script/style() | Libraries API (.libraries.yml) |
| Build | @wordpress/scripts (webpack) | Babel/drupal-js-build (.es6.js) |
| Media | Native media library | Drupal file entities + optional media module |
composer require 'drupal/gutenberg:^3.0'
drush en gutenberg
Then enable Gutenberg for specific content types at /admin/config/content/gutenberg.
When modifying a block's save() function, always add a deprecation entry to prevent "Attempt Block Recovery" warnings on existing content.
Gutenberg compares stored HTML against the current save() output. A mismatch — even a single added class — triggers a validation warning on every existing instance of that block. The deprecated API tells Gutenberg how to recognize old markup and silently migrate it.
save(), copy the current save function and attributes object into a versioned constant:
const v1 = {
attributes: { /* current attributes, before your change */ },
save({ attributes }) {
// Current save() body, before your change
},
};
deprecated array: deprecated: [v1]migrate(attributes) function that maps old to newdeprecated: [v2, v1]Drupal Gutenberg replaces wp.i18n with a Drupal.t() wrapper (gutenberg/js/i18n.js). The vendored i18n filter hooks (i18n.gettext) and setLocaleData() are inoperative. To override Gutenberg UI strings, monkey-patch wp.i18n.__ directly.
See these files in this skill's directory:
.info.yml, .libraries.yml, .gutenberg.ymlServerSideRenderProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub khawkins98/drupal-gutenberg-llm-skills --plugin drupal-gutenberg