# BricksBuilder Plugins

# The Complete Guide to Developing Plugins & Extensions for Bricks Builder

Bricks is a visual, theme‑based site builder for WordPress. Unlike some other builders, Bricks ships with a genuinely developer‑friendly PHP/JS API. You can extend it by **registering custom elements**, **adding controls to existing elements**, **creating dynamic data tags**, and **hooking into dozens of action and filter hooks**. This guide walks through every major extension point, with working code, best practices, and distribution advice.

Everything below is based on the official **Bricks Academy Developer documentation** (`academy.bricksbuilder.io/developer`).

---

## 1. How Bricks Extensibility Works (Mental Model)

Before writing code, understand the three "worlds" your code lives in:

| Context | What runs there | Notes |
|---|---|---|
| **Builder panel** (the editing UI) | Vue.js + your PHP `set_controls()` / `set_control_groups()` definitions | Controls are defined in PHP but rendered by Vue. Optionally you can supply a Vue `x-template` for live preview. |
| **Canvas** (live preview inside builder) | Your `render()` output + frontend scripts | The iframe that shows the rendered page while editing. |
| **Frontend** (the public page) | Your `render()` output + `enqueue_scripts()` | What visitors actually see. |

A **Bricks element** is a PHP class that extends `\Bricks\Element`. It's conceptually very similar to a WordPress widget: you declare properties, define settings ("controls"), and implement a `render()` method that echoes HTML.

Your extension code can live in one of two places:

- **A Bricks child theme** — simplest, officially recommended for small customizations.
- **A standalone WordPress plugin** — best for distributable/reusable add‑ons (a true "Bricks plugin"). This is the focus of most of this guide.

> ⚠️ **Golden rule:** Never edit the Bricks parent theme files directly — updates will wipe your changes. Always use a child theme, a code‑snippets plugin, or your own plugin.

---

## 2. Prerequisites

- A licensed, working copy of the **Bricks theme** (custom elements require the theme active).
- Comfort with **PHP**, WordPress **hooks (actions/filters)**, and basic **HTML/CSS**. JavaScript is needed for interactive elements.
- A local dev environment (LocalWP, DevKinsta, wp-env, Docker, etc.).
- Recommended WordPress settings per Bricks best practices:
  - **Permalinks → Post name** (Bricks builds builder URLs from permalinks).
  - Upload images ≥ 600px wide (ideally ≥ 1600px), and run **Regenerate Thumbnails** if adding Bricks to an existing site.

---

## 3. Two Ways to Ship Your Code

### Option A — Bricks Child Theme (quick start)

1. Download `bricks-child.zip` from your **Bricks account** (`my.bricksbuilder.io`).
2. **Appearance → Themes → Add New → Upload**, then activate **Bricks Child Theme**.
3. The child theme already includes a **demo custom element** and a working `functions.php` to learn from.

The child theme's `functions.php` is loaded **in addition to** (not instead of) the parent theme's, right before it.

### Option B — Standalone Plugin (recommended for add‑ons)

A plugin is portable across themes/sites and is how commercial Bricks add‑ons (e.g., Bricksforge, Advanced Themer) are shipped. A minimal plugin scaffold:

```
my-bricks-addon/
├── my-bricks-addon.php          ← main plugin file (header + bootstrap)
├── elements/
│   └── element-test.php         ← one file per custom element
├── assets/
│   ├── js/
│   │   └── test-element.js
│   └── css/
│       └── test-element.css
└── includes/
    └── dynamic-tags.php
```

**Main plugin file** (`my-bricks-addon.php`):

```php
<?php
/**
 * Plugin Name:       My Bricks Add-on
 * Description:       Custom elements and dynamic data for Bricks Builder.
 * Version:           1.0.0
 * Author:            Your Name
 * Requires PHP:      7.4
 * Text Domain:       my-bricks-addon
 */

if ( ! defined( 'ABSPATH' ) ) exit; // No direct access

define( 'MBA_PATH', plugin_dir_path( __FILE__ ) );
define( 'MBA_URL',  plugin_dir_url( __FILE__ ) );
define( 'MBA_VER',  '1.0.0' );

/**
 * Only boot our Bricks integration if the Bricks theme is active.
 */
add_action( 'init', function () {

    // Bail early if Bricks isn't present.
    if ( ! class_exists( '\Bricks\Elements' ) ) {
        return;
    }

    // Register our custom elements.
    $element_files = [
        MBA_PATH . 'elements/element-test.php',
    ];

    foreach ( $element_files as $file ) {
        \Bricks\Elements::register_element( $file );
    }

    // Load other integrations (dynamic tags, etc.).
    require_once MBA_PATH . 'includes/dynamic-tags.php';

}, 11 ); // Priority 11 => after Bricks registers its own elements.
```

The key API call is identical whether you use a child theme or a plugin:

```php
\Bricks\Elements::register_element( $file, $name = null, $class = null );
```

`register_element()` accepts up to **3 arguments**:

- `$file` *(required)* — absolute server path to the element PHP file.
- `$name` *(optional)* — the element's name string, e.g. `prefix-element-test`.
- `$element_class` *(optional)* — the PHP class name, e.g. `Prefix_Element_Test`, which must extend `\Bricks\Element`.

> The `class_exists( '\Bricks\Elements' )` guard is what makes a plugin safe: if someone deactivates Bricks or switches themes, your plugin won't fatal‑error.

---

## 4. Anatomy of a Custom Element

Custom elements follow a pattern very similar to WordPress widgets: **extend `\Bricks\Element`, fill in properties, define controls, implement `render()`.**

### 4.1 The blank skeleton

Create `elements/element-test.php`:

```php
<?php
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

class Prefix_Element_Test extends \Bricks\Element {

    // ---- Element properties ----
    public $category     = '';
    public $name         = '';
    public $icon         = '';
    public $css_selector = '';
    public $scripts      = [];
    public $nestable     = false; // true => nestable (@since 1.5)

    // ---- Builder-specific methods ----
    public function get_label() {}
    public function get_keywords() {}
    public function set_control_groups() {}
    public function set_controls() {}

    // ---- Frontend-specific methods ----
    public function enqueue_scripts() {}
    public function render() {}
}
```

### 4.2 Element properties reference

| Property | Required | Purpose |
|---|---|---|
| `$category` | ✅ | Lowercase, no spaces. Use a built‑in category (`general`, `media`, etc.) or your own. If custom, provide a translatable label via the `bricks/builder/i18n` filter. |
| `$name` | ✅ | Unique identifier, lowercase, no spaces. **Always prefix** to avoid clashes (e.g. `prefix-test`). Bricks adds a CSS class `.brxe-{name}` to your element. |
| `$icon` | — | Icon font CSS class shown in the builder panel. Bricks bundles **Font Awesome 6**, **Ionicons 4**, and **Themify Icons** (e.g. `ti-bolt-alt`, `fas fa-anchor`). |
| `$css_selector` | — | By default, style controls target the element wrapper. Set this to redirect the default CSS selector to a child element. |
| `$nestable` | — | `false` for plain elements; `true` to allow the element to contain other elements via drag & drop. |
| `$scripts` | — | Array of JS function names to run when the element renders on the frontend or updates in the builder. **Prefix them** (e.g. `prefixElementTest`). |

### 4.3 Builder methods reference

| Method | Required | Purpose |
|---|---|---|
| `get_label()` | ✅ | Return the localized element name shown in the panel. |
| `get_keywords()` | — | Array of search keywords so users can find the element. |
| `set_control_groups()` | — | Group your controls into collapsible sections under the Content/Style tabs. |
| `set_controls()` | ✅ | Define the element's settings (controls). |
| `enqueue_scripts()` | — | Enqueue element‑specific CSS/JS — loaded **only** on pages using the element (great for performance). |
| `render()` | ✅ | Echo the element's HTML. |

### 4.4 Built‑in render helpers

These helper methods (inherited from `\Bricks\Element`) are the backbone of a clean `render()`:

| Helper | What it does |
|---|---|
| `set_attribute( $key, $attribute, $value )` | Register an HTML attribute for a tag. `$key` is a unique identifier for that tag (`'_root'` is special — it holds the element ID/classes). `$value` can be a string or array. |
| `render_attributes( $key )` | Output all attributes previously set for `$key`. |
| `render_dynamic_data_tag( $tag, $context, $args )` | Resolve a single dynamic tag such as `{post_title}` with the correct post context. |
| `render_dynamic_data( $content )` | Resolve any dynamic tags contained inside a string, using the correct post ID for the current render context. |

---

## 5. A Complete, Working Element (Fully Populated)

Here's the full example from the docs, an "alert box"‑style element with a content field and a type selector:

```php
<?php
if ( ! defined( 'ABSPATH' ) ) exit;

class Prefix_Element_Test extends \Bricks\Element {

    // Element properties
    public $category     = 'general';                // Predefined category
    public $name         = 'prefix-test';            // Always prefix!
    public $icon         = 'ti-bolt-alt';            // Themify icon
    public $css_selector = '.prefix-test-wrapper';   // Default CSS target
    public $scripts      = [ 'prefixElementTest' ];  // JS to run on render

    // Localized label
    public function get_label() {
        return esc_html__( 'Test element', 'bricks' );
    }

    // Search keywords
    public function get_keywords() {
        return [ 'alert', 'notice', 'message', 'box' ];
    }

    // Control groups (sections in the panel)
    public function set_control_groups() {
        $this->control_groups['text'] = [
            'title' => esc_html__( 'Text', 'bricks' ),
            'tab'   => 'content', // "content" or "style"
        ];

        $this->control_groups['settings'] = [
            'title' => esc_html__( 'Settings', 'bricks' ),
            'tab'   => 'content',
        ];
    }

    // Controls (the actual settings)
    public function set_controls() {

        $this->controls['content'] = [
            'tab'     => 'content',
            'group'   => 'text',
            'label'   => esc_html__( 'Content', 'bricks' ),
            'type'    => 'text',
            'default' => esc_html__( 'Content goes here ..', 'bricks' ),
        ];

        $this->controls['type'] = [
            'tab'         => 'content',
            'group'       => 'settings',
            'label'       => esc_html__( 'Type', 'bricks' ),
            'type'        => 'select',
            'options'     => [
                'info'    => esc_html__( 'Info', 'bricks' ),
                'success' => esc_html__( 'Success', 'bricks' ),
                'warning' => esc_html__( 'Warning', 'bricks' ),
                'danger'  => esc_html__( 'Danger', 'bricks' ),
                'muted'   => esc_html__( 'Muted', 'bricks' ),
            ],
            'inline'      => true,
            'clearable'   => false,
            'pasteStyles' => false,
            'default'     => 'info',
        ];
    }

    // Enqueue this element's assets (only on pages that use it)
    public function enqueue_scripts() {
        wp_enqueue_script( 'prefix-test-script' );
    }

    // Render the HTML output
    public function render() {

        $root_classes[] = 'prefix-test-wrapper';

        if ( ! empty( $this->settings['type'] ) ) {
            $root_classes[] = "color-{$this->settings['type']}";
        }

        // Attach classes to the special '_root' tag
        $this->set_attribute( '_root', 'class', $root_classes );

        // '_root' is REQUIRED (contains element ID, classes, etc.)
        echo "<div {$this->render_attributes( '_root' )}>";

            if ( ! empty( $this->settings['content'] ) ) {
                echo "<div>{$this->settings['content']}</div>";
            }

        echo '</div>';
    }
}
```

### Key things to notice

- **`$this->settings`** is an associative array keyed by your control IDs (`content`, `type`). This is how you read what the user entered.
- **`_root`** is the reserved attribute key for the outer wrapper. You **must** echo `{$this->render_attributes( '_root' )}` on your root tag — it injects the element's unique ID and the `.brxe-{name}` class Bricks needs for styling and functionality.
- CSS from style controls automatically applies to `.brxe-prefix-test` (or the `$css_selector` you set) — so styles "just work" if `_root` attributes are rendered.

---

## 6. Element Controls In Depth

Controls are defined in `set_controls()`. Each control is an array keyed by a unique control ID.

### 6.1 Universal control arguments (apply to every control type)

| Argument | Type | Default | Description |
|---|---|---|---|
| `tab` | string | `content` | `content` or `style`. |
| `group` | string | — | Which control group to display under. |
| `label` | string | — | Localized label. |
| `type` | string | — | The control type (see list below). |
| `inline` | bool | `false` | Label and input on the same line. |
| `small` | bool | `false` | Narrow (60px) input. |
| `css` | array | — | CSS rule(s) auto‑generated from the value. Each rule needs `property` + `selector` (and optional `important`). |
| `default` | string/array | — | Default value (shape depends on control type). |
| `pasteStyles` | bool | `true` | Set `false` for content‑producing controls so they're excluded from "Paste Styles". |
| `description` | string | — | Help text under the control. |
| `required` | array | — | Conditional display based on another control's value. |

### 6.2 The `css` argument — auto CSS generation

Many controls can generate CSS for you without touching `render()`. Example of a color control that writes `color` onto `.content`:

```php
$this->controls['testColor'] = [
    'tab'         => 'content',
    'group'       => 'settings',
    'label'       => esc_html__( 'Text color', 'bricks' ),
    'type'        => 'color',
    'inline'      => true,
    'small'       => true,
    'css'         => [
        [
            'property'  => 'color',
            'selector'  => '.content',
            'important' => true, // optional
        ],
    ],
    'default'     => [
        'rgb' => 'rgba(158, 158, 158, .8)',
        'hex' => '#9e9e9e',
    ],
    'pasteStyles' => false,
    'description' => esc_html__( 'Define the content color.', 'bricks' ),
    'required'    => [ 'showText', '!=', '' ],
];
```

### 6.3 The `required` argument — conditional controls

`required` shows/hides a control based on another control's value:

```php
'required' => ['layout', '=', ['list', 'grid']],
// Show this control only when the "layout" control equals "list" OR "grid".
```

Comparison operators: `=`, `!=`, `>`, `<`, `>=`, `<=`.

### 6.4 Available control types

Content‑producing, CSS‑producing, or both:

| Control | Output |
|---|---|
| `apply`, `info` | Panel only |
| `text`, `textarea`, `editor`, `code`, `link`, `icon`, `svg`, `image`, `image-gallery`, `audio`, `datepicker`, `repeater`, `posts` | Content |
| `color`, `background`, `gradient`, `border`, `box-shadow`, `text-shadow`, `filters`, `dimensions`, `direction`, `typography`, `align-items`, `justify-content`, `text-align`, `text-decoration`, `text-transform` | CSS |
| `number`, `select`, `image`, `checkbox` | Content and/or CSS/Conditional |

Each type has extra type‑specific arguments documented under `academy.bricksbuilder.io/developer/controls/`.

---

## 7. Adding Frontend JavaScript to Your Element

For interactive elements, register scripts referenced by `$scripts` and enqueue them in `enqueue_scripts()`.

**In the element class:**

```php
public $scripts = [ 'prefixElementTest' ]; // JS function name

public function enqueue_scripts() {
    wp_enqueue_script( 'prefix-test-script' );
    wp_enqueue_style(  'prefix-test-style'  );
}
```

**Register the assets** (in your plugin bootstrap, hooked to `wp_enqueue_scripts`):

```php
add_action( 'wp_enqueue_scripts', function () {
    wp_register_script(
        'prefix-test-script',
        MBA_URL . 'assets/js/test-element.js',
        [ 'bricks-scripts' ], // depend on Bricks frontend if needed
        MBA_VER,
        true
    );
    wp_register_style(
        'prefix-test-style',
        MBA_URL . 'assets/css/test-element.css',
        [],
        MBA_VER
    );
} );
```

**The JS file** — the function named in `$scripts` is invoked by Bricks per element instance. A common, robust pattern:

```javascript
// assets/js/test-element.js
function prefixElementTest() {
  document.querySelectorAll('.brxe-prefix-test').forEach((el) => {
    // Initialize each instance of the element here
    el.addEventListener('click', () => el.classList.toggle('is-open'));
  });
}

// Run on frontend load AND when Bricks re-renders in the builder
document.addEventListener('DOMContentLoaded', prefixElementTest);
```

> **Builder vs frontend gotcha:** When enqueuing CSS that shouldn't leak into the builder UI, guard with `bricks_is_builder_main()`:
>
> ```php
> add_action( 'wp_enqueue_scripts', function () {
>     if ( ! bricks_is_builder_main() ) {
>         wp_enqueue_style( 'my-frontend-only', /* ... */ );
>     }
> } );
> ```

---

## 8. Nestable Elements (containers that hold other elements)

Since **Bricks 1.5**, elements can be **nestable** — meaning users can drag other elements inside them (like the Accordion, Slider, and Tabs elements). This is what you want for cards, sliders, layout wrappers, etc.

### 8.1 Enable nesting

```php
public $nestable = true;
```

This tells Bricks to use the nestable render path and enable drag & drop inside the element.

### 8.2 Define the default inner structure with `get_nestable_children()`

Return an array of element definitions that populate the element when it's first dropped in:

```php
public function get_nestable_children() {
    return [
        [
            'name'     => 'block',
            'label'    => esc_html__( 'Slide', 'bricks' ) . ' {item_index}',
            'settings' => [
                '_hidden' => [
                    '_cssClasses' => 'hidden-class', // CSS class, hidden from UI
                ],
            ],
            'children' => [
                [
                    'name'     => 'heading',
                    'settings' => [
                        'text' => esc_html__( 'Slide', 'bricks' ) . ' {item_index}',
                    ],
                ],
                [
                    'name'     => 'button',
                    'settings' => [
                        'text'  => esc_html__( 'I am a button', 'bricks' ),
                        'size'  => 'lg',
                        'style' => 'primary',
                    ],
                ],
            ],
        ],
    ];
}
```

- `name` is a Bricks element name (`block`, `heading`, `button`, etc.).
- `children` recursively nests more elements.
- `settings` pre‑populates each child.
- `{item_index}` is a placeholder token for numbering repeated items.

### 8.3 Render children in PHP

Use the `Frontend::render_children()` helper, passing `$this`:

```php
public function render() {
    $output  = "<div {$this->render_attributes( '_root' )}>";
    $output .= \Bricks\Frontend::render_children( $this ); // renders nested items
    $output .= '</div>';
    echo $output;
}
```

### 8.4 (Optional) Live builder preview with a Vue x-template

For a live preview inside the builder, output a Vue template that includes `<bricks-element-children>`:

```php
public function render_builder() { ?>
    <script type="text/x-template" id="tmpl-bricks-element-custom-nestable">
        <component :is="tag">
            <h2>Title before nestable children</h2>
            <bricks-element-children :element="element"/>
            <p>Text node after nestable children</p>
        </component>
    </script>
<?php }
```

### 8.5 (Optional) Repeater for same‑level items

If your nestable structure is a list of same‑level items (like accordion items), add a repeater control bound to `children`:

```php
public function set_controls() {
    $this->controls['_children'] = [
        'type'          => 'repeater',
        'titleProperty' => 'label',
        'items'         => 'children', // @since 1.5
    ];
}
```

---

## 9. Extending *Existing* Bricks Elements (without replacing them)

You don't always need a brand‑new element. Since **Bricks 1.3.2**, you can inject controls into any core element with the filter `bricks/elements/{element_name}/controls`:

```php
add_filter( 'bricks/elements/posts/controls', function ( $controls ) {
    $controls['ignoreStickyPosts'] = [
        'tab'   => 'content',
        'group' => 'query',
        'label' => esc_html__( 'Ignore Sticky Posts', 'my-bricks-addon' ),
        'type'  => 'checkbox',
    ];
    return $controls;
} );
```

Related filters for tweaking existing elements:

- **`bricks/elements/{element_name}/control_groups`** — add/modify groups.
- **`bricks/element/settings`** — modify an element's settings just before render.
- **`bricks/element/render`** and **`bricks/frontend/render_element`** — filter the rendered HTML.
- **`bricks/element/render_attributes`** — modify attributes.
- **`bricks/posts/query_vars`** (and `bricks/{object_type}s/query_vars`) — alter the query that a query‑loop element runs.

---

## 10. Creating Custom Dynamic Data Tags

Dynamic Data tags (like `{post_title}`) let content update automatically. You can register your own, e.g. `{my_dd_tag}`.

### Step 1 — Register the tag in the builder UI

```php
add_filter( 'bricks/dynamic_tags_list', function ( $tags ) {
    $tags[] = [
        'name'  => '{my_dd_tag}',            // prefix to keep unique
        'label' => 'My Dynamic Data',
        'group' => 'My Dynamic Data Group',
    ];
    return $tags;
} );
```

### Step 2 — Resolve a single tag with `bricks/dynamic_data/render_tag`

```php
add_filter( 'bricks/dynamic_data/render_tag', 'get_my_tag_value', 20, 3 );

function get_my_tag_value( $tag, $post, $context = 'text' ) {
    if ( ! is_string( $tag ) ) {
        return $tag;
    }

    $clean_tag = str_replace( [ '{', '}' ], '', $tag );

    if ( $clean_tag !== 'my_dd_tag' ) {
        return $tag; // not ours; pass through untouched
    }

    return run_my_dd_tag_logic();
}

function run_my_dd_tag_logic() {
    return 'My dynamic data value';
}
```

### Step 3 — Resolve tags embedded in larger strings

`bricks/dynamic_data/render_content` and `bricks/frontend/render_data` fire when a string may contain multiple tags mixed with HTML:

```php
add_filter( 'bricks/dynamic_data/render_content', 'render_my_tag', 20, 3 );
add_filter( 'bricks/frontend/render_data',        'render_my_tag', 20, 2 );

function render_my_tag( $content, $post, $context = 'text' ) {
    if ( strpos( $content, '{my_dd_tag}' ) === false ) {
        return $content;
    }
    $value = run_my_dd_tag_logic();
    return str_replace( '{my_dd_tag}', $value, $content );
}
```

### Tags with arguments (e.g. `{my_dd_tag:foo}`)

Parse arguments after the colon:

```php
add_filter( 'bricks/dynamic_data/render_tag', function ( $tag, $post, $context = 'text' ) {
    if ( ! is_string( $tag ) ) return $tag;

    $clean = str_replace( [ '{', '}' ], '', $tag );
    if ( strpos( $clean, 'my_dd_tag:' ) !== 0 ) return $tag;

    $argument = str_replace( 'my_dd_tag:', '', $clean );
    return run_my_dd_tag_logic( $argument );
}, 20, 3 );
```

For content strings containing argument‑style tags, use a regex to find and replace each occurrence:

```php
preg_match_all( '/{(my_dd_tag:[^}]+)}/', $content, $matches );
foreach ( $matches[1] as $i => $match ) {
    $full  = $matches[0][ $i ];
    $value = get_my_tag_value( $match, $post, $context );
    $content = str_replace( $full, $value, $content );
}
```

---

## 11. Hooks Reference (Actions & Filters)

Bricks exposes a large hook API. Below are the most useful ones for plugin developers, grouped by purpose.

### 11.1 Action hooks (structure & lifecycle)

| Hook | Fires… |
|---|---|
| `bricks_before_site_wrapper` / `bricks_after_site_wrapper` | Around the whole site wrapper. |
| `bricks_before_header` / `bricks_after_header` | Around the header template. |
| `bricks_before_footer` / `bricks_after_footer` | Around the footer template. |
| `bricks_body` | Just inside `<body>`. |
| `bricks_meta_tags` | In the `<head>` for meta output. |
| `bricks/load_elements/before` / `bricks/load_elements/after` | Around element registration — handy for late registration. |
| `bricks/frontend/before_render_data` / `after_render_data` | Around content rendering. |
| `bricks/query/before_loop` / `after_loop` | Around query‑loop iterations. |
| `bricks/form/custom_action` | Run custom logic on a Form element submission. |
| `bricks/dynamic_data/tags_registered` | After all dynamic tags register. |

### 11.2 Filter hooks (behavior & data)

| Filter | Use it to… |
|---|---|
| `bricks/builder/i18n` | Provide a translatable label for a **custom element category**. |
| `bricks/elements/{name}/controls` | Add controls to an existing element. |
| `bricks/elements/{name}/control_groups` | Add control groups to an existing element. |
| `bricks/element/settings` | Modify element settings before render. |
| `bricks/element/render` / `bricks/frontend/render_element` | Filter rendered element HTML. |
| `bricks/element/render_attributes` | Modify HTML attributes. |
| `bricks/dynamic_tags_list` | Register a custom dynamic data tag. |
| `bricks/dynamic_data/render_tag` / `render_content` | Resolve custom tags. |
| `bricks/dynamic_data/register_providers` | Register a whole dynamic data provider class. |
| `bricks/posts/query_vars` / `bricks/{type}s/query_vars` | Modify query‑loop queries. |
| `bricks/query/result` / `result_count` | Alter query results. |
| `bricks/code/allow_execution` / `disable_execution` / `disallow_keywords` | Control the Code element's PHP execution (security). |
| `bricks/svg/allowed_tags` / `allowed_attributes` / `bypass_sanitization` | Control SVG sanitization. |
| `bricks/builder/supported_post_types` | Enable Bricks editing on custom post types. |
| `bricks/conditions/groups` / `options` / `result` | Extend the conditional‑visibility system. |
| `bricks/setup/control_options` | Add reusable global control option sets. |

> The full documented list contains 200+ filters and ~35 actions. Browse them at `academy.bricksbuilder.io/developer/hooks/`.

### 11.3 Registering a custom element category with a translatable label

If you use a non‑built‑in `$category`, register its label:

```php
add_filter( 'bricks/builder/i18n', function ( $i18n ) {
    $i18n['myaddon'] = esc_html__( 'My Add-on', 'my-bricks-addon' );
    return $i18n;
} );
```

Then set `public $category = 'myaddon';` on your element.

---

## 12. Reading & Generating Bricks Data (Advanced)

Bricks stores page structure as an array of element definitions in post meta. The **Schema** section of the docs describes this data model. This matters when you:

- Programmatically generate templates or elements.
- Build import/export tooling.
- Provide default nestable children (as shown earlier).

Each element node generally looks like:

```php
[
    'id'       => 'abc123',          // unique element id
    'name'     => 'heading',         // element type
    'parent'   => 'parentId',        // parent element id (0 for top-level)
    'children' => [ /* child ids */ ],
    'settings' => [ /* control values */ ],
]
```

Helper functions worth knowing (documented under `/developer/functions/`):

- `bricks_render_dynamic_data( $content, $post_id, $context )` — resolve dynamic tags in an arbitrary string outside an element.
- `bricks_is_builder_main()` / `bricks_is_builder()` — detect the builder context (crucial for asset loading).

---

## 13. Best Practices, Security & Conventions

**Naming & prefixing**
- Prefix everything: element `$name`, `$scripts` function names, control IDs where collisions are likely, dynamic tags, and hook callbacks. This prevents conflicts with core and other add‑ons.

**Internationalization**
- Wrap all user‑facing strings in `esc_html__()` / `__()` with your text domain.

**Security**
- Add `if ( ! defined( 'ABSPATH' ) ) exit;` at the top of every PHP file.
- **Escape output** in `render()`: use `esc_html()`, `esc_attr()`, `esc_url()`, and `wp_kses_post()` as appropriate. Never echo raw `$this->settings` values into HTML without escaping.
- **Sanitize** any data you save.
- Respect Bricks' Code‑element security filters if your add‑on executes user PHP.

**Performance**
- Enqueue assets in the element's `enqueue_scripts()` so they load **only** on pages that use the element.
- Register (`wp_register_script/style`) globally, then `wp_enqueue_*` within the element.
- Guard builder‑unfriendly CSS with `bricks_is_builder_main()`.

**Compatibility & robustness**
- Always guard integration code with `class_exists( '\Bricks\Elements' )`.
- Register elements on `init` at **priority 11** (after Bricks loads its own).
- Declare `Requires PHP` and test against the Bricks versions you support (note version‑gated features like nestable @since 1.5).

**Don't edit core**
- Never modify the parent theme. Use a plugin, child theme, or code‑snippet plugin.

---

## 14. Testing & Debugging Workflow

1. **Enable debugging** in `wp-config.php`:
   ```php
   define( 'WP_DEBUG', true );
   define( 'WP_DEBUG_LOG', true );
   define( 'SCRIPT_DEBUG', true ); // load unminified assets
   ```
2. **Verify registration:** open the builder, search your element by label/keywords in the elements panel.
3. **Check both contexts:** confirm the element renders correctly in the **canvas** and on the **live frontend** (they can differ).
4. **Inspect the wrapper:** confirm your root tag has the `.brxe-{name}` class and unique ID — if not, you likely forgot `render_attributes( '_root' )`.
5. **Watch the console** for JS errors when your `$scripts` function runs.
6. **Study the source:** the Bricks theme's own element files (`accordion-nested.php`, `slider-nested.php`, `tabs-nested.php`, etc.) are the best reference for advanced patterns. The child theme also ships a demo element.

---

## 15. Packaging & Distributing a Bricks Add‑on

- Ship as a **standard WordPress plugin** (portable across themes).
- Keep the Bricks guard so the plugin degrades gracefully without Bricks.
- Follow WordPress plugin structure and headers; add an **update mechanism** (e.g., your own update server, or freemius/EDD for commercial).
- Version your elements and consider a settings page to toggle which elements load.
- Document required Bricks version, especially for version‑gated APIs (nestable @since 1.5, add‑controls filter @since 1.3.2).
- If you want a no‑/low‑code path, tools like **Bricksforge**, **WDesignKit**, and **Advanced Themer** provide visual "element/widget builders" that generate Bricks elements — useful for prototyping, but hand‑coded elements give the most control.

---

## Quick‑Reference Checklist

| Task | API / Hook |
|---|---|
| Register a custom element | `\Bricks\Elements::register_element( $file, $name, $class )` on `init` (priority 11) |
| Base class to extend | `\Bricks\Element` |
| Required methods | `get_label()`, `set_controls()`, `render()` |
| Required render output | `render_attributes( '_root' )` on the root tag |
| Group controls | `set_control_groups()` |
| Auto‑generate CSS from a control | `css` argument on the control |
| Conditional control | `required` argument |
| Make element a container | `$nestable = true` + `get_nestable_children()` + `Frontend::render_children($this)` |
| Add controls to core elements | `bricks/elements/{name}/controls` |
| Custom dynamic tag | `bricks/dynamic_tags_list` + `bricks/dynamic_data/render_tag` + `render_content` |
| Custom element category label | `bricks/builder/i18n` |
| Detect builder for asset loading | `bricks_is_builder_main()` |
| Resolve dynamic data in a string | `bricks_render_dynamic_data()` / `$this->render_dynamic_data()` |

---

### Official Resources
- **Developer hub:** `academy.bricksbuilder.io/developer/`
- **Create elements:** `/developer/elements/create-your-own-elements/`
- **Nestable elements:** `/developer/elements/nestable-elements/`
- **Controls reference:** `/developer/controls/`
- **Hooks (actions & filters):** `/developer/hooks/`
- **Dynamic data tags:** `/developer/dynamic-data/create-your-own-dynamic-data-tag/`
- **Child theme guide:** `/developer/guides/child-theme/`

With the element API, the controls system, nestable containers, dynamic data tags, and the extensive hook library, you have everything needed to build a full‑featured, distributable Bricks Builder add‑on. Start from the child‑theme demo element, graduate to a standalone plugin structure, and layer in nestable behavior, dynamic data, and hooks as your add‑on grows.