Skip to content
JS Filters

lzb.editor.controls.render

Filters the whole list of rendered controls for one placement, group and nesting level. Reach for it to group controls into sections of your own, or to rebuild the list from the raw control data instead of patching elements you cannot inspect.

Note the plural. lzb.editor.control.render filters a single control, takes three arguments, and runs only after the type-specific lzb.editor.control.CONTROL_TYPE.render filter returned something truthy. This one filters the finished list, takes two arguments, and always runs, including when nothing rendered.

Attributes

NameTypeDescription
resultArray | JSXrendered controls, or one PanelBody wrapping them when the placement is inspector
dataObjectplacement, group, childOf, childIndex, getControls, renderControl
NameTypeDescription
placementStringinspector or content
groupStringdefault, content, list, styles or advanced
childOfStringuid of the parent control, '' at the top level
childIndexNumber | Booleanrepeater row index, false outside a repeater
getControlsFunctiongetControls(childOf) returns the raw controls of that level, keyed by uid
renderControlFunctionrenderControl(control, placement, uid, childIndex, group) renders one control

A control stored with the group settings is normalised to default before the filter runs, so match on default and never on settings. Content placement and repeater rows are both rendered without a group argument, so they always arrive as group: 'default'.

When placement is inspector and at least one control rendered, result is a single PanelBody element rather than the array. Check for that before treating the value as a list.

Usage

The Pro Panel control throws the default result away and rebuilds the list, which is what getControls and renderControl are there for.

JS
const { PanelBody } = wp.components;
 
wp.hooks.addFilter(
  "lzb.editor.controls.render",
  "my.custom.namespace",
  function (result, data) {
    const {
      placement,
      group,
      childOf,
      childIndex,
      getControls,
      renderControl,
    } = data;
 
    if (placement !== "inspector") {
      return result;
    }
 
    const controls = getControls(childOf);
    const main = [];
    const advanced = [];
 
    Object.keys(controls).forEach((uid) => {
      const rendered = renderControl(
        controls[uid],
        placement,
        uid,
        childIndex,
        group,
      );
 
      if (!rendered) {
        return;
      }
 
      if (controls[uid].name.indexOf("advanced_") === 0) {
        advanced.push(rendered);
      } else {
        main.push(rendered);
      }
    });
 
    if (!advanced.length) {
      return result;
    }
 
    return [
      <PanelBody key="main">{main}</PanelBody>,
      <PanelBody key="advanced" title="Advanced" initialOpen={false}>
        {advanced}
      </PanelBody>,
    ];
  },
);

renderControl returns false for a control whose placement or group does not match the arguments you passed, so drop the falsy results rather than pushing them into the list. Whatever you return is rendered as it stands, which means an array without a key on each element makes React warn on every editor render.

Was this article helpful?

Copyright © 2026 Lazy Blocks.