| name | bricks-elements |
| description | Create custom elements for Bricks Builder extending the Element class. |
Bricks Elements Skill
This skill covers creating custom elements for Bricks Builder.
Element Architecture
All elements extend the abstract \Bricks\Element class and live in includes/elements/.
Element Class Structure
<?php
namespace Bricks;
if ( ! defined( 'ABSPATH' ) ) exit;
class Element_My_Custom extends Element {
public $category = 'general';
public $name = 'my-custom';
public $icon = 'ti-star';
public $tag = 'div';
public $scripts = [];
public $css_selector = '';
public $draggable = true;
public $deprecated = false;
public $nestable = false;
public function get_label() {
return esc_html__( 'My Custom Element', 'bricks' );
}
public function get_keywords() {
return [ 'custom', 'my', 'element' ];
}
public function set_control_groups() {
}
public function set_controls() {
}
public function render() {
}
public static function render_builder() {
}
}
Registering Custom Elements
Method 1: Filter Hook (Recommended)
add_filter( 'bricks/builder/elements', function( $element_names ) {
$element_names[] = 'my-custom';
return $element_names;
} );
add_action( 'init', function() {
require_once 'path/to/element-my-custom.php';
}, 11 );
Method 2: Element Class Registration
add_action( 'init', function() {
\Bricks\Elements::register_element( 'path/to/element-my-custom.php' );
}, 11 );
Element Categories
Built-in categories:
basic - Basic elements (heading, text, button, image, video)
general - General elements (divider, icon-box, accordion, tabs, form, etc.)
media - Media elements (image-gallery, audio, carousel, slider)
wordpress - WordPress elements (posts, terms, navigation)
single - Single post elements (post-title, post-content, post-meta)
woocommerce - WooCommerce elements (if active)
Custom Category
add_filter( 'bricks/builder/element_categories', function( $categories ) {
$categories['my-category'] = [
'title' => 'My Custom',
'icon' => 'ti-package',
];
return $categories;
} );
Control Types
Basic Controls
public function set_controls() {
$this->controls['title'] = [
'tab' => 'content',
'label' => esc_html__( 'Title', 'bricks' ),
'type' => 'text',
'default' => 'Hello World',
'placeholder' => 'Enter title...',
];
$this->controls['description'] = [
'tab' => 'content',
'label' => esc_html__( 'Description', 'bricks' ),
'type' => 'textarea',
];
$this->controls['count'] = [
'tab' => 'content',
'label' => esc_html__( 'Count', 'bricks' ),
'type' => 'number',
'min' => 1,
'max' => 100,
'step' => 1,
'default' => ,
=> ,
];
->controls[] = [
=> ,
=> ( , ),
=> ,
=> ,
=> [
[
=> ,
=> ,
],
],
];
->controls[] = [
=> ,
=> ( , ),
=> ,
];
->controls[] = [
=> ,
=> ( , ),
=> ,
=> [
=> ( , ),
=> ( , ),
=> ,
],
=> ,
=> ( , ),
];
->controls[] = [
=> ,
=> ( , ),
=> ,
=> [
[
=> ,
=> ,
],
],
];
}
Media Controls
$this->controls['image'] = [
'tab' => 'content',
'label' => esc_html__( 'Image', 'bricks' ),
'type' => 'image',
];
$this->controls['gallery'] = [
'tab' => 'content',
'label' => esc_html__( 'Gallery', 'bricks' ),
'type' => 'image-gallery',
];
$this->controls['icon'] = [
'tab' => 'content',
'label' => esc_html__( 'Icon', 'bricks' ),
'type' => 'icon',
];
$this->controls['video'] = [
'tab' => 'content',
'label' => esc_html__( 'Video', 'bricks' ),
'type' => 'video',
];
$this->controls['svg'] = [
'tab' => 'content',
'label' => esc_html__( , ),
=> ,
];
Advanced Controls
$this->controls['link'] = [
'tab' => 'content',
'label' => esc_html__( 'Link', 'bricks' ),
'type' => 'link',
];
$this->controls['typography'] = [
'tab' => 'content',
'label' => esc_html__( 'Typography', 'bricks' ),
'type' => 'typography',
'css' => [
[
'property' => 'font',
'selector' => '.text',
],
],
];
$this->controls['itemPadding'] = [
'tab' => 'content',
'label' => esc_html__( 'Padding', 'bricks' ),
'type' => 'spacing',
'css' => [
[
'property' => 'padding',
'selector' => '.item',
],
],
];
$this->controls['border'] = [
'tab' => 'content',
=> ( , ),
=> ,
=> [
[
=> ,
=> ,
],
],
];
->controls[] = [
=> ,
=> ( , ),
=> ,
=> [
[
=> ,
=> ,
],
],
];
->controls[] = [
=> ,
=> ( , ),
=> ,
=> [
[
=> ,
=> ,
],
],
];
->controls[] = [
=> ,
=> ( , ),
=> ,
=> ,
=> ,
];
Control Groups
public function set_control_groups() {
$this->control_groups['settings'] = [
'title' => esc_html__( 'Settings', 'bricks' ),
'tab' => 'content',
];
$this->control_groups['styling'] = [
'title' => esc_html__( 'Styling', 'bricks' ),
'tab' => 'content',
];
}
public function set_controls() {
$this->controls['option1'] = [
'tab' => 'content',
'group' => 'settings',
'label' => esc_html__( 'Option 1', 'bricks' ),
'type' => 'text',
];
}
Conditional Controls
$this->controls['customTag'] = [
'tab' => 'content',
'label' => esc_html__( 'Custom tag', 'bricks' ),
'type' => 'text',
'required' => [ 'tag', '=', 'custom' ],
];
$this->controls['advancedOption'] = [
'tab' => 'content',
'label' => esc_html__( 'Advanced', 'bricks' ),
'type' => 'text',
'required' => [
[ 'showAdvanced', '=', true ],
[ 'mode', '!=', 'simple' ],
],
];
Separator Control
$this->controls['iconSeparator'] = [
'tab' => 'content',
'label' => esc_html__( 'Icon', 'bricks' ),
'type' => 'separator',
];
Repeater Control
$this->controls['items'] = [
'tab' => 'content',
'label' => esc_html__( 'Items', 'bricks' ),
'type' => 'repeater',
'titleProperty' => 'title',
'fields' => [
'title' => [
'label' => esc_html__( 'Title', 'bricks' ),
'type' => 'text',
],
'content' => [
'label' => esc_html__( 'Content', 'bricks' ),
'type' => 'textarea',
],
'icon' => [
'label' => esc_html__( 'Icon', 'bricks' ),
'type' => 'icon',
],
],
'default' => [
[ 'title' => 'Item 1', 'content' => 'Content 1' ],
[ 'title' => 'Item 2', 'content' => 'Content 2' ],
],
];
Rendering Elements
Basic Render Method
public function render() {
$settings = $this->settings;
$this->set_attribute( '_root', 'class', 'my-custom-element' );
if ( ! empty( $settings['layout'] ) ) {
$this->set_attribute( '_root', 'class', 'layout-' . $settings['layout'] );
}
$output = "<{$this->tag} {$this->render_attributes( '_root' )}>";
if ( isset( $settings['title'] ) ) {
$output .= '<h3 class="title">' . esc_html( $settings['title'] ) . '</h3>';
}
$output .= "</{$this->tag}>";
echo $output;
}
Working with Dynamic Data
public function render() {
$settings = $this->settings;
$title = isset( $settings['title'] )
? $this->render_dynamic_data( $settings['title'] )
: '';
$content = bricks_render_dynamic_data( $settings['content'], get_the_ID(), 'text' );
echo "<div>{$title}</div>";
}
Working with Links
public function render() {
$settings = $this->settings;
if ( ! empty( $settings['link'] ) ) {
$this->set_link_attributes( 'link', $settings['link'] );
echo "<a {$this->render_attributes( 'link' )}>Link Text</a>";
}
}
Rendering Icons
public function render() {
$settings = $this->settings;
if ( ! empty( $settings['icon'] ) ) {
$icon_html = self::render_icon( $settings['icon'] );
echo $icon_html;
}
}
Rendering Images
public function render() {
$settings = $this->settings;
if ( ! empty( $settings['image'] ) ) {
$image_html = $this->render_image( $settings['image'], [
'size' => 'large',
'class' => 'my-image',
] );
echo $image_html;
}
}
Builder Preview (Vue Template)
public static function render_builder() { ?>
<script type="text/x-template" id="tmpl-bricks-element-my-custom">
<div :class="['my-custom-element', settings.layout ? `layout-${settings.layout}` : null]">
<contenteditable
tag="h3"
class="title"
:name="name"
controlKey="title"
:settings="settings"
/>
<div v-if="settings.showContent" class="content">
{{ settings.content }}
</div>
<icon-svg v-if="settings.icon?.icon" :iconSettings="settings.icon"/>
</div>
</script>
<?php
}
Nestable Elements
For elements that can contain child elements:
class Element_My_Container extends Element {
public $nestable = true;
public $vue_component = 'bricks-nestable';
public function get_nestable_children() {
return [
[
'name' => 'div',
'settings' => [
'_padding' => [ 'top' => '20px', 'bottom' => '20px' ],
],
],
];
}
public function render() {
$output = "<{$this->tag} {$this->render_attributes( '_root' )}>";
$output .= \Bricks\Frontend::render_children( $this );
$output .= "</{$this->tag}>";
echo $output;
}
}
Query Loop Elements
For elements that loop through posts/terms/users:
class Element_My_Posts extends Custom_Render_Element {
public $category = 'wordpress';
public $name = 'my-posts';
public function render() {
$settings = $this->settings;
$query = new \Bricks\Query( $this->element );
$output = '<div class="posts-wrapper">';
$query->render( function( $query_obj ) use ( $settings ) {
$post_id = get_the_ID();
echo '<div class="post-item">';
echo '<h3>' . get_the_title( $post_id ) . '</h3>';
echo '</div>';
} );
$output .= '</div>';
$query->destroy();
echo $output;
}
}
Enqueuing Scripts
class Element_My_Slider extends Element {
public $scripts = [ 'bricksSplide' ];
public function enqueue_scripts() {
wp_enqueue_script(
'my-slider',
plugin_dir_url( __FILE__ ) . 'js/slider.js',
[ 'jquery' ],
'1.0.0',
true
);
wp_enqueue_style(
'my-slider',
plugin_dir_url( __FILE__ ) . 'css/slider.css',
[],
'1.0.0'
);
}
}
Element Hooks
Filters
add_filter( 'bricks/elements/my-custom/controls', function( $controls ) {
$controls['newOption'] = [
'tab' => 'content',
'label' => 'New Option',
'type' => 'text',
];
return $controls;
} );
add_filter( 'bricks/elements/my-custom/control_groups', function( $groups ) {
$groups['extra'] = [
'title' => 'Extra Settings',
'tab' => 'content',
];
return $groups;
} );
add_filter( 'bricks/elements/my-custom/scripts', function( $scripts ) {
$scripts[] = 'bricksIsotope';
return $scripts;
} );
add_filter( 'bricks/frontend/render_element', function( $html, $element ) {
if ( $element->name === 'my-custom' ) {
$html = '<div class="wrapper">' . $html . '</div>';
}
return ;
}, , );
Complete Element Example
<?php
namespace Bricks;
if ( ! defined( 'ABSPATH' ) ) exit;
class Element_Feature_Box extends Element {
public $category = 'general';
public $name = 'feature-box';
public $icon = 'ti-layout-cta-center';
public $tag = 'div';
public function get_label() {
return esc_html__( 'Feature Box', 'bricks' );
}
public function get_keywords() {
return [ 'feature', 'box', 'card', 'icon' ];
}
public function set_control_groups() {
$this->control_groups['icon'] = [
'title' => esc_html__( 'Icon', 'bricks' ),
=> ,
];
}
{
->controls[] = [
=> ,
=> ( , ),
=> ,
=> ,
];
->controls[] = [
=> ,
=> ( , ),
=> ,
=> ,
];
->controls[] = [
=> ,
=> ( , ),
=> ,
];
->controls[] = [
=> ,
=> ,
=> ( , ),
=> ,
];
->controls[] = [
=> ,
=> ,
=> ( , ),
=> ,
=> ,
=> [
[
=> ,
=> ,
],
],
];
->controls[] = [
=> ,
=> ,
=> ( , ),
=> ,
=> [
[
=> ,
=> ,
],
],
];
}
{
= ->settings;
->( , , );
= ;
( ! ( [] ) ) {
.= . ::( [] ) . ;
}
( ! ( [] ) ) {
= ->( [] );
.= . . ;
}
( ! ( [] ) ) {
= ->( [] );
.= . . ;
}
( ! ( [] ) ) {
->( , [] );
.= ;
}
.= ;
;
}
{
<script type= id=>
<div
Best Practices
- Namespace: Always use
namespace Bricks;
- Unique names: Element
$name must be unique across all elements
- Escape output: Use
esc_html(), esc_attr(), wp_kses_post() appropriately
- Dynamic data: Use
$this->render_dynamic_data() for user content
- Attributes: Use
$this->set_attribute() and $this->render_attributes()
- CSS: Use the
css property in controls for automatic style generation
- Builder preview: Provide
render_builder() for better editing experience
- Keywords: Add relevant keywords for element search