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)
- Download
bricks-child.zipfrom your Bricks account (my.bricksbuilder.io). - Appearance → Themes → Add New → Upload, then activate Bricks Child Theme.
- The child theme already includes a demo custom element and a working
functions.phpto 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
/**
* 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:
\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
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
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->settingsis an associative array keyed by your control IDs (content,type). This is how you read what the user entered._rootis 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_selectoryou set) — so styles "just work" if_rootattributes 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:
$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:
'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:
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):
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:
// 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():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
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:
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',
],
],
],
],
];
}
nameis a Bricks element name (block,heading,button, etc.).childrenrecursively nests more elements.settingspre‑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:
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>:
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:
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:
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;
} );
bricks/elements/{element_name}/control_groups— add/modify groups.bricks/element/settings— modify an element's settings just before render.bricks/element/renderandbricks/frontend/render_element— filter the rendered HTML.bricks/element/render_attributes— modify attributes.bricks/posts/query_vars(andbricks/{object_type}s/query_vars) — alter the query that a query‑loop element runs.
10. Creating Custom Dynamic Data Tags
Step 1 — Register the tag in the builder UI
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
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:
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:
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:
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:
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:
[
'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,$scriptsfunction 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(): useesc_html(),esc_attr(),esc_url(), andwp_kses_post()as appropriate. Never echo raw$this->settingsvalues 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, thenwp_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
initat priority 11 (after Bricks loads its own). - Declare
Requires PHPand 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
- Enable debugging in
wp-config.php:define( 'WP_DEBUG', true ); define( 'WP_DEBUG_LOG', true ); define( 'SCRIPT_DEBUG', true ); // load unminified assets - Verify registration: open the builder, search your element by label/keywords in the elements panel.
- Check both contexts: confirm the element renders correctly in the canvas and on the live frontend (they can differ).
- Inspect the wrapper: confirm your root tag has the
.brxe-{name}class and unique ID — if not, you likely forgotrender_attributes( '_root' ). - Watch the console for JS errors when your
$scriptsfunction runs. - 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.