---
title: "lzb.constructor.panels"
description: "JS filter `lzb.constructor.panels` of the Lazy Blocks WordPress plugin."
url: "https://www.lazyblocks.com/docs/js-filters/lzb-constructor-panels/"
source: "js-filters/lzb-constructor-panels.mdx"
---
# lzb.constructor.panels

Filters the collapsible panels the block builder puts in the Inspector under the general settings. Add a panel here when an extension keeps its own data on the block and needs somewhere in the builder to edit it.

The default array holds four panels in this order: `style-variations`, `supports`, `supports-ghost-kit` and `condition`. All four start collapsed.

## Attributes

| Name     | Type       | Description                                |
| -------- | ---------- | ------------------------------------------ |
| `panels` | **Array**  | panel definitions, rendered in array order |
| `data`   | **Object** | `blockData`, `updateBlockData`             |

Each panel is an object with four keys:

| Name          | Type         | Description                                                       |
| ------------- | ------------ | ----------------------------------------------------------------- |
| `name`        | **String**   | React key of the panel, and the handle other extensions match on  |
| `title`       | **String**   | heading shown on the panel                                        |
| `component`   | **Function** | React component type, rendered with `data` and `updateData` props |
| `initialOpen` | **Boolean**  | whether the panel is expanded on first render                     |

`component` is the component itself, not an element. The builder writes `` inside a `PanelBody`, so a panel that sets `component` to `` hands React an element where it expects a type and nothing renders.

`data.updateBlockData` shallow merges the object you give it into the block data, so a panel writes one field with `updateData({ my_field: value })`.

## Usage

The Relationships panel finds the `condition` panel and splices itself in after it, which keeps the ordering stable when other extensions add panels too.

```js title="JS"
const { TextControl } = wp.components;

function TrackingPanel({ data, updateData }) {
  return (
    <TextControl
      label="Tracking ID"
      value={data.my_tracking_id || ""}
      onChange={(value) => updateData({ my_tracking_id: value })}
    />
  );
}

wp.hooks.addFilter(
  "lzb.constructor.panels",
  "my.custom.namespace",
  function (panels) {
    const conditionIndex = panels.findIndex(
      (panel) => panel.name === "condition",
    );
    const insertIndex =
      conditionIndex === -1 ? panels.length : conditionIndex + 1;

    panels.splice(insertIndex, 0, {
      name: "my-tracking",
      title: "Tracking",
      component: TrackingPanel,
      initialOpen: false,
    });

    return panels;
  },
);
```

The builder calls `panels.map()` on the result, so returning anything but an array throws `panels.map is not a function` and leaves the block builder Inspector blank. A handler that decides to do nothing must return the incoming `panels`.

## Documentation Index
> Fetch the complete documentation index at: https://www.lazyblocks.com/llms.txt
> Fetch every page in a single file at: https://www.lazyblocks.com/llms-full.txt
