Docs Panel registry

Panel Registry: Using wptPanels in block.json

The wptPanels key in block.json is how a block opts into the registry. It controls which panels appear in the block’s inspector and allows per-block default overrides.


Option 1: Full Registry (Recommended)

"wptPanels": {
  "structure": true,
  "spacing":   true,
  "layout":    true
}

true means: use this panel, pull all options and defaults from the registry. When the admin changes registry options, this block gets the changes on next re-registration. This is the correct default for all new blocks.


Option 2: Disable a Panel

"wptPanels": {
  "structure": true,
  "spacing":   false,
  "layout":    false
}

false means: do not register any attributes or render any inspector panel for this panel group. Use this for blocks that genuinely do not need spacing or visibility control, for example, a simple inline element or a block that manages its own layout entirely.


Option 3: Override Specific Defaults

"wptPanels": {
  "structure": {
    "headingLevel": "h3",
    "semanticRole": "article"
  },
  "spacing": {
    "blockPadding": "xl"
  },
  "layout": true
}

An object overrides the default values for the fields you specify. The available options still come from the registry, so if the admin adds a new spacing value, it appears in this block’s dropdown too. Only the starting default is changed.

This is ideal for blocks that are semantically always a certain type, a team member card is always an article, for example, so its default semanticRole should be article.

The object override changes defaults only, not available options. To restrict available options for a specific block, you need to declare the controls manually in index.js instead of using the registry for that field.


Reading the Registry in index.js

( function () {
  'use strict';

  var REGISTRY = window.wptPanelRegistry || {};

  // Build SelectControl options from a registry field
  function registryOptions( panel, fieldKey ) {
    var fields = REGISTRY[panel] && REGISTRY[panel].fields ? REGISTRY[panel].fields : [];
    for ( var i = 0; i < fields.length; i++ ) {
      if ( fields[i].key === fieldKey && fields[i].options ) {
        return fields[i].options.map( function(v) { return { label: v, value: v }; } );
      }
    }
    return [];
  }

  // Build all option arrays upfront
  var HEADING_OPTIONS  = registryOptions( 'structure', 'headingLevel' );
  var ROLE_OPTIONS     = registryOptions( 'structure', 'semanticRole' );
  var PADDING_OPTIONS  = registryOptions( 'spacing',   'blockPadding' );
  var GAP_OPTIONS      = registryOptions( 'spacing',   'blockGap' );
  var MB_OPTIONS       = registryOptions( 'spacing',   'spacingBottom' );
  var ALIGN_OPTIONS    = registryOptions( 'layout',    'textAlign' );

  // Use in SelectControl
  el( SelectControl, {
    label:    'Heading Level',
    value:    attributes.headingLevel,
    options:  HEADING_OPTIONS,
    onChange: function(v) { setAttributes({ headingLevel: v }); }
  })

}() );

Falls back to an empty array if the registry is unavailable. The block still renders; no JS crash.