Custom module developer guide

Creating custom modules for Beaver Builder can open up a world of possibilities for you and your clients. You can extend and modify our existing modules to fit a specific use case, or create brand new modules.

To get started, you can download our example plugin below and explore the code or read through the step-by-step guide provided on this page.

Download the Example Plugin

Table of Contents

Overview

In this step-by-step guide, we'll go over how to code custom modules with their own settings panel just like the modules included with the builder.

A module reference and setting field reference is available at the end of this guide to further assist in your custom module coding.

Additionally, the steps below aren't the only way to create a custom module. If you wish, you can override any of the built-in modules by following the steps in the Overriding Built-In Modules section.

1. Create a plugin

To create custom modules, you will first need to create a simple plugin. This will ensure that your modules are not overwritten when updating the builder via the remote updates interface.

To create a plugin, navigate to the WordPress plugins directory located in wp-content/plugins and create a new folder. You should give this folder a unique name using lowercase letters and dashes such as my-builder-modules.

Next, create a PHP file within your new folder and give it the same name such as my-builder-modules.php.

Finally, copy and paste the code below into your plugin's PHP file and edit the information such as Plugin Name and Author. You should also rename the constants MY_MODULES_DIR and MY_MODULES_URL to match your plugin's namespace, keeping the _DIR and _URL suffixes.

/**
 * Plugin Name: My Custom Modules
 * Plugin URI: http://www.mywebsite.com
 * Description: Custom modules for the Beaver Builder Plugin.
 * Version: 1.0
 * Author: Your Name
 * Author URI: http://www.mywebsite.com
 */
define( 'MY_MODULES_DIR', plugin_dir_path( __FILE__ ) );
define( 'MY_MODULES_URL', plugins_url( '/', __FILE__ ) );

function my_load_module_examples() {
    if ( class_exists( 'FLBuilder' ) ) {
        // Include your custom modules here.
    }
}
add_action( 'init', 'my_load_module_examples' );

Now that you've created a plugin, you can activate it within the WordPress admin and move on to the next step.

2. Add a module to your plugin

To add a module, create a folder in your plugin folder and give it a unique name using lowercase letters and dashes such as my-module.

Important: Module folders and file names must be namespaced with a prefix to avoid conflicts with core modules. For example, if your name is Justin Busa and you would like to create a button module, your module path would be jb-button/jb-button.php, because a non-prefixed  button/button. php would be overridden by the core button module.  If two modules have the same folder name or slug, even if they are in different plugins, there will be a conflict. 

Next, create a PHP file within your module folder and give it the same name, such as my-module.php.

Finally, you will need to configure your module's class so it can be registered with the builder in the next step. To do so, copy and paste the code below into your module's PHP file. Make sure to rename this class to reflect the name of your module and change out the module info in the construct method.

For more detailed information about the module class, please see the Module Property Reference and Module Method Reference sections.

class MyModuleClass extends FLBuilderModule {

    public function __construct()
    {
        parent::__construct(array(
            'name'            => __( 'My Module', 'fl-builder' ),
            'description'     => __( 'A totally awesome module!', 'fl-builder' ),
            'category'        => __( 'Advanced Modules', 'fl-builder' ),
            'dir'             => MY_MODULES_DIR . 'my-module/',
            'url'             => MY_MODULES_URL . 'my-module/',
            'editor_export'   => true, // Defaults to true and can be omitted.
            'enabled'         => true, // Defaults to true and can be omitted.
            'partial_refresh' => false, // Defaults to false and can be omitted.
        ));
    }
}

Once you've configured your module class, you'll need to include it within your plugin. To do so, open your main plugin file and include it as shown in the following example.

function my_load_module_examples() {
    if ( class_exists( 'FLBuilder' ) ) {
        require_once 'my-module/my-module.php';
    }
}

Now that you've added a module to your plugin, it's time to move on to the next step to register it with the builder and create the settings form.

3. Define module settings

Registering your module is done through the FLBuilder::register_module method call. That method accepts two parameters: the name of your module class and an associative array of information for building your settings form.

Example:

FLBuilder::register_module( 'MyModuleClass', array() );

The settings array

The settings array is a nested set of information that allows you to define tabs, sections within your tabs and fields within your sections. The top-level array items should be associative arrays with the slug for the tabs as the array keys. The title of the tabs should also be defined within these arrays as shown in the example below.

Example:

FLBuilder::register_module( 'MyModuleClass', array(
    'my-tab-1'      => array(
        'title'         => __( 'Tab 1', 'fl-builder' ),
    ),
    'my-tab-2'      => array(
        'title'         => __( 'Tab 2', 'fl-builder' ),
    ),
) );

Within your tab arrays should be another array that defines your sections, with the slug for the sections as the array keys. The title of the sections should also be defined within these arrays, as shown in the example below.

Example:

FLBuilder::register_module( 'MyModuleClass', array(
    'my-tab-1'      => array(
        'title'         => __( 'Tab 1', 'fl-builder' ),
        'sections'      => array(
            'my-section-1'  => array(
                'title'            => __( 'Section 1', 'fl-builder' ),
            ),
            'my-section-2'  => array(
                'title'            => __( 'Section 2', 'fl-builder' ),
            )
        )
    )
) );

Within your section arrays should be another array that defines your fields, with the slug for the fields as the array keys. Please see the Settings Field Reference for an in-depth look at all of the field types as well as additional configuration properties.

Example:

FLBuilder::register_module( 'MyModuleClass', array(
    'my-tab-1'      => array(
        'title'         => __( 'Tab 1', 'fl-builder' ),
        'sections'      => array(
            'my-section-1'  => array(
                'title'         => __( 'Section 1', 'fl-builder' ),
                'fields'        => array(
                    'my-field-1'     => array(
                        'type'          => 'text',
                        'label'         => __( 'Text Field 1', 'fl-builder' ),
                    ),
                    'my-field-2'     => array(
                        'type'          => 'text',
                        'label'         => __( 'Text Field 2', 'fl-builder' ),
                    )
                )
            )
        )
    )
) );

Note that the "slug" for tabs, sections and fields should be unique or you will run into conflicts. For example, you will run into issues if you have two tabs that use the slug "my-tab-1".

4. Module settings CSS and JavaScript

Now that you've defined your settings, you're ready to customize them using CSS and JavaScript. The builder gives you two entry points for doing this. Note that it isn't necessary to include any CSS or JavaScript for your settings to work. Doing so is optional.

Module settings CSS - my-plugin/my-module/css/settings.css

Any CSS you add to this file will be loaded when the settings panel for your module is loaded.

Module settings JavaScript - my-plugin/my-module/js/settings.js

The JavaScript in this file will be loaded when the settings panel for your module is loaded. This is where you can register a helper object for your settings for initialization, validation rules, and prechecking before the form is submitted. Please see the example located in example/js/settings.js for detailed info and examples of working with form settings JavaScript.

5. Module HTML

A module only needs the main module file and the frontend HTML file to work. This file is required and should be located at my-plugin/my-module/includes/frontend.php. It is used to render the HTML for each individual instance of your module and provides access to the full WordPress environment in addition to the following variables.

$module object
An instance of your module class that has all of the properties and methods of the parent FLBuilderModule class in addition to the properties and methods that you define.

$id string
The module's node ID.

$settings object
An object that contains the module settings you defined when registering your module. Use these to output content or check for certain conditions before doing so.

Example:

<div class="fl-example-text">
    <?php echo $settings->textarea_field; ?>
    <?php $module->example_method(); ?>
</div>

6. Module CSS

Now that you have a completed module, it's time to style it! The builder gives you three entry points for defining CSS for your module, all of which are optional and don't need to be included for your module to work. These entry points are as follows.

Global CSS – my-plugin/my-module/css/frontend.css

This file should contain styles that will be applied to all module instances within a builder layout.

Global responsive CSS – my-plugin/my-module/css/frontend.responsive.css

This file should contain styles that will be applied to all module instances within a builder layout once the responsive breakpoint has been reached. The responsive breakpoint can be set or disabled in the global settings panel.

Instance CSS – my-plugin/my-module/includes/frontend.css.php

This file is used to render the CSS for each individual instance of your module. Note that this is CSS that applies to each instance, such as a color, not global styles that should be applied to all instances. Use css/frontend.css if you would like to include global module styles for your layouts.

In addition to the full WordPress environment, within this file, you have access to these variables…

$module object
An instance of your module class that has all of the properties and methods of the parent FLBuilderModule class in addition to the properties and methods that you define.

$id string
The module's node ID.

$settings object
An object that contains the module settings you defined when registering your module. Use these to output styles or check for certain conditions before doing so.

Example:

.fl-node-<?php echo $id; ?> {
    background-color: #'<?php echo $settings->bg_color; ?>';
}

7. Module JavaScript

The builder gives you two entry points for defining JavaScript for your module, both of which are optional and don't need to be included for your module to work. 

Global JavaScript – my-plugin/my-module/js/frontend.js

This file should contain JavaScript that will be applied to all module instances within a builder layout.

Instance JavaScript – my-plugin/my-module/includes/frontend.js.php

This file is used to render the JavaScript for each individual instance of your module. Note that this is JavaScript that applies to each instance, not global JavaScript that should be applied to all instances. Use js/frontend.jss if you would like to include global JavaScript for your module.

In addition to the full WordPress environment, within this file, you have access to the following variables:

$module object
An instance of your module class that has all of the properties and methods of the parent FLBuilderModule class in addition to the properties and methods that you define.

$id string
The module's node ID.

$settings object
An object that contains the module settings you defined when registering your module. Use these to output JavaScript or check for certain conditions before doing so.

Example:

console.log('Module ID: <?php echo $id; ?>');
console.log('Text: <?php echo $settings->text_field; ?>');

Module property reference

$category string
The category that your module will appear in the builder's module list.

$description string
A short description of what your modules does. It is not currently used within the builder but may be in the future.

$dir string
The directory path to you module. This should include the trailing slash.

$editor_export boolean
Set this to false if you do not want a stripped down version of your module exported to the default WordPress editor when publishing a layout.

$enabled boolean
Set this to false to disable your module and keep it from appearing in the builder's module list.

$name string
The name of your module that will appear in the builder's module list.

$node string
The module's unique ID that doesn't change, even if it is exported and imported into another database.

$partial_refresh boolean
Set this to true if you would like to enable partial refresh for your module. Please see the section on partial refresh before doing so.

$url string
The URL path to your module. This should include the trailing slash.

Module method reference

add_css( $handle, $src, $deps, $ver, $media )
Use this method to register and enqueue additional styles for your module. Using this method ensures that your styles will only be loaded when the module is present on the page, instead of loading it on every page. This method can be used just like you would use the wp_enqueue_style function.

Example:

// Already registered by the builder.
$this->add_css( 'font-awesome' );

// Register and enqueue your own.
$this->add_css( 'example-lib', $this->url . 'css/example-lib.css' );

add_js( $handle, $src, $deps, $ver, $in_footer )
Use this method to register and enqueue additional scripts for your module. Using this method ensures that your scripts will only be loaded when the module is present on the page, instead of loading it on every page. This method can be used just like you would use the wp_enqueue_script function.

Example:

// Already registered by the builder.
$this->add_js( 'jquery-bxslider' ); 

// Register and enqueue your own.
$this->add_js( 'example-lib', $this->url . 'js/example-lib.js', array(), '', true );

remove()
This method should not be called directly by developers. It is called by the builder when a module is being removed from the page. Developers should override this method in their module class if they need to work with a module before it is removed from the page.

Example:

public function remove() {     
    if( !empty( $this->settings->photo_path ) )
        unlink( $this->settings->photo_path );
}

delete()
This method should not be called directly by developers. It is called by the builder when a module is being deleted. Developers should override this method in their module class if they need to work with a module before it is deleted.

Note: This method is called when a module is updated and when it's removed from the page and should be used for things like clearing photo cache from the builder's cache directory. If you only need to run logic when a module is actually removed from the page, use the remove method instead.

Example:

public function delete() {     
    if( !empty( $this->settings->photo_path ) )
        unlink( $this->settings->photo_path );
}

enqueue_scripts()
This method should not be called directly by developers. It is called by the builder when module scripts and styles are being enqueued. Developers should override this method in their module class if they need to conditionally enqueue styles and scripts for their modules.

Example:

public function enqueue_scripts()
{
    if ( $this->settings && $this->settings->link_type == 'lightbox' ) {
        $this->add_js( 'jquery-magnificpopup' );
        $this->add_css( 'jquery-magnificpopup' );
    }
}

update( $settings )
This method should not be called directly by developers. It is called by the builder when a module is being updated and passed the module settings that can be modified before they are saved to the database. Developers should override this method in their module class if they need to work with settings before they are saved.

Example:

public function update( $settings ) {

    if( empty( $settings->my_field ) )
        $settings->my_field = 'Default Text';  

    return $settings;
}

Setting fields reference

Note: Don't use either the type or connection key in a setting field. Those two keys are reserved for internal use.

Code

The code field displays an Ace editor for working with code.

Return value
A string containing the user-submitted code.

'my_code_field' => array(
    'type'          => 'code',
    'editor'        => 'html',
    'rows'          => '18'
),

Color

The color field displays a color picker that can be used to pick a custom color. If show_reset is set to true, users will also be able to clear the color which results in an empty value. You can also set show_alpha to true to show the alpha slider in the picker.

Return value
The hexadecimal color value excluding the hash (#) sign.

'my_color_field' => array(
    'type'          => 'color',
    'label'         => __( 'Color Picker', 'fl-builder' ),
    'default'       => '333333',
    'show_reset'    => true,
    'show_alpha'    => true
),

Editor

The editor field displays a WordPress editor that can be used to insert text just as you would on the post edit screen. If media_buttons is set to true, the Add Media button will be available above the editor.

Return value
The HTML content generated by the editor.

'my_editor_field' => array(
    'type'          => 'editor',
    'media_buttons' => true,
    'rows'          => 10,
    'wpautop'       => true
),

I you would like to enable oEmbed in your editor fields, set the wpautop param to false and output your text in frontend.php like so...

global $wp_embed;
echo wpautop( $wp_embed->autoembed( $settings->text ) );

Font

The font field displays a select for choosing a font family and a select for choosing a corresponding weight.

Return value
An array with the family and weight values as shown below.

Array
(
    [family] => Helvetica
    [weight] => 300
)
'my_font_field' => array(
    'type'          => 'font',
    'label'         => __( 'Font', 'fl-builder' ),
    'default'       => array(
        'family'        => 'Helvetica',
        'weight'        => 300
    )
),

Form

A form field is a nested form that can be launched from within an existing form. For a live example of this, check out the Items tab of the Accordion module. There you will see that when you click the Edit Item link, a nested form is displayed for you to edit that particular item.

Return value
An object containing a property for each of the settings defined in the form.

Usage
In order to use the form field, you must have a form defined. That is done similar to registering a module but instead uses the FLBuilder::register_settings_form method. That method takes your form ID as well as an array of form config data (again similar to a module's form).

FLBuilder::register_settings_form('my_form_field', array(
    'title' => __('My Form Field', 'fl-builder'),
    'tabs'  => array(
        'general'      => array(
            'title'         => __('General', 'fl-builder'),
            'sections'      => array(
                'general'       => array(
                    'title'         => '',
                    'fields'        => array(
                        'label'         => array(
                            'type'          => 'text',
                            'label'         => __('Label', 'fl-builder')
                        )
                    )
                ),
            )
        )
    )
));

Once you have a form defined, you can reference it in your field's config array by setting the form value to the ID of the form you registered. You should also set the preview_text value to the ID of a field that is defined in your form.

'my_form_field' => array(
    'type'          => 'form',
    'label'         => __('My Form', 'fl-builder'),
    'form'          => 'my_form_field', // ID of a registered form.
    'preview_text'  => 'label', // ID of a field to use for the preview text.
)

Icon

The icon field displays an icon selector that can be used to pick a custom icon. If show_remove is set to true, users will also be able to clear the icon which results in an empty value.

Return value
The icon class names, such as fa fa-flag.

'my_icon_field' => array(
    'type'          => 'icon',
    'label'         => __( 'Icon Field', 'fl-builder' ),
    'show_remove'   => true
),

Link

The link field displays a text input for entering a URL and a select button that shows a dialog when clicked for selecting a post of any type to link to.

Return value
The link entered by the user.

'my_link_field' => array(
    'type'          => 'link',
    'label'         => __('Link Field', 'fl-builder')
),

Loop settings

Loop settings aren't actually a single field but a collection of fields that make up a tab in the settings panel. That tab allows users to select things like Post Type, Order, Terms and more that you can use to create a WordPress loop using the FLBuilderLoop::query method.

Return value
All of the values for the loop settings will be stored in your module's settings object. You shouldn't need to access those settings directly (although you can if you want to). Instead, just pass them to the FLBuilderLoop::query method, as shown below, and it will take care of everything for you.

Usage
To use the loop settings, simply create a new tab array that links to the loop-settings.php file instead of defining sections and fields like a standard tab array.

'my_loop_settings' => array(
    'title'         => __( 'Loop Settings', 'fl-builder' ),
    'file'          => FL_BUILDER_DIR . 'includes/loop-settings.php',
)

When a user saves your module, you can create a loop in your frontend.php file, as follows:

$query = FLBuilderLoop::query( $settings );
    
if ( $query->have_posts() ) {
    while ( $query->have_posts() ) {
        $query->the_post();
    }
}

wp_reset_postdata();

You can also display pagination for your loop:

if ( $query->have_posts() ) {
    FLBuilderLoop::pagination( $query );
}

Multiple audios

The multiple audios field displays a WordPress media lightbox that allows users to select multiple audio files.

Return value
A JSON-encoded array of attachment IDs for each audio file selected.

'my_multiple_audios_field' => array(
    'type'          => 'multiple-audios',
    'label'         => __( 'Multiple Audios Field', 'fl-builder' )
),

Multiple photos

The multiple photos field displays a WordPress media lightbox that allows users to select multiple photos.

Return value
A JSON encoded array of attachment ids for each photo selected.

'my_multiple_photos_field' => array(
    'type'          => 'multiple-photos',
    'label'         => __( 'Multiple Photos Field', 'fl-builder' )
),

Photo

The photo field displays a WordPress media lightbox that allows users to select a single photo.

Return value
The attachment ID for the selected photo. In addition to the attachment ID, the photo field also provides the photo URL as a separate variable in your module's settings object. That variable has the same name as your photo field with the suffix _src appended to it, such as my_photo_field_src. This is useful if all you need is the URL and don't need to deal with the attachment ID or data. If show_remove is set to true, users will also be able to clear the photo which results in an empty value.

'my_photo_field' => array(
    'type'          => 'photo',
    'label'         => __('Photo Field', 'fl-builder')
    'show_remove'	=> false
),

Photo sizes

The photo sizes field displays a select input containing the available WordPress photo sizes as values.

Return value
The photo size string that can be used in functions such as the_post_thumbnail($size) to retrieve a specific photo size.

'my_photo_sizes_field' => array(
    'type'          => 'photo-sizes',
    'label'         => __('Photo Sizes Field', 'fl-builder'),
    'default'       => 'medium'
),

Post type

The post type field displays a select input containing the available WordPress post types as values.

Return value
The post type slug, such as post or page.

'my_post_type_field' => array(
    'type'          => 'post-type',
    'label'         => __('Post Type', 'fl-builder'),
    'default'       => 'post'
),

Select

The select field displays a standard HTML select input. Select fields can also be setup to show/hide other tabs, sections and fields within your settings by using the toggle array as shown in the example below. If the value selected by the user matches one defined in your toggle array, those tabs, sections and fields will be shown; otherwise, they will be hidden.

Return value
The value selected by the user.

'my_select_field' => array(
    'type'          => 'select',
    'label'         => __( 'Select Field', 'fl-builder' ),
    'default'       => 'option-1',
    'options'       => array(
        'option-1'      => __( 'Option 1', 'fl-builder' ),
        'option-2'      => __( 'Option 2', 'fl-builder' )
    ),
    'toggle'        => array(
        'option-1'      => array(
            'fields'        => array( 'my_field_1', 'my_field_2' ),
            'sections'      => array( 'my_section' ),
            'tabs'          => array( 'my_tab' )
        ),
        'option-2'      => array()
    )
),

You can also make a select field that has the ability to select multiple values by setting the multi-select config value to true as shown below. If that is set, this field will return an array of selected values.

'my_multi_select_field' => array(
    'type'          => 'select',
    'label'         => __( 'Multi Select Field', 'fl-builder' ),
    'default'       => 'option-1',
    'options'       => array(
        'option-1'      => __( 'Option 1', 'fl-builder' ),
        'option-2'      => __( 'Option 2', 'fl-builder' )
    ),
    'multi-select'  => true
),

Service

The service field isn't actually a single field but a collection of fields that make up a section in a settings form tab. These fields allow users to enter account information for a specific service (such as MailChimp) and save API connection data. You can then use the saved data in your modules to do things such as subscribe someone to a mailing list.

Return value
The values for the service fields will be stored in your module's settings object.

Usage
To use the service fields, simply create a new section array that links to the service-settings.php file instead of defining fields like a standard section array. You also need to define the type of services that you would like available in the services list. The builder currently supports the autoresponder type.

'service' => array(
    'title'         => __( 'Service Settings', 'fl-builder' ),
    'file'          => FL_BUILDER_DIR . 'includes/service-settings.php',
    'services'      => 'autoresponder'
),

Once your module is saved, you can access the service instance:

// Subscribe to an autoresponder service.
$instance = FLBuilderServices::get_service_instance( $settings->service );
$response = $instance->subscribe( $settings, $email, $name );

You can also access the saved account data by doing the following...

$instance = FLBuilderServices::get_service_instance( $settings->service );
$account_data = $instance->get_account_data( $settings->service_account );

Suggest

The suggest field displays a text input that returns a list of suggested results as the user types. It is currently designed to work with posts (of any type), terms (tags, categories, etc.), users and links (to a post of any type).

The action value of the config array currently accepts one of four values...

  • fl_as_posts - Search posts. The data value of the config array must be the slug of the post type you want to search. Returns an array of post IDs.
  • fl_as_terms - Search terms. The data value of the config array must be the slug of the term you want to search. Returns an array of term IDs.
  • fl_as_users - Search users. Returns an array of user IDs.
  • fl_as_links - Search links. Returns an array of permalinks.

Return value
A string of comma-separated values that the user has selected.

// Search all pages.
'my_suggest_field' => array(
    'type'          => 'suggest',
    'label'         => __( 'Suggest Field', 'fl-builder' ),
    'action'        => 'fl_as_posts', // Search posts.
    'data'          => 'page', // Slug of the post type to search.
    'limit'         => 3, // Limits the number of selections that can be made.
),

Text

The text field displays a standard HTML text input.

Return value
The text entered by the user.

'my_text_field' => array(
    'type'          => 'text',
    'label'         => __( 'Text Field', 'fl-builder' ),
    'default'       => '',
    'maxlength'     => '2',
    'size'          => '3',
    'placeholder'   => __( 'Placeholder text', 'fl-builder' ),
    'class'         => 'my-css-class',
    'description'   => __( 'Text displayed after the field', 'fl-builder' ),
    'help'          => __( 'Text displayed in the help tooltip', 'fl-builder' )
),

Textarea

The textarea field displays a standard HTML textarea input.

Return value
The text entered by the user.

'my_textarea_field' => array(
    'type'          => 'textarea',
    'label'         => __( 'Textarea Field', 'fl-builder' ),
    'default'       => '',
    'placeholder'   => __( 'Placeholder Text', 'fl-builder' ),
    'rows'          => '6'
),

Time

The time field displays inputs for selecting the hour, minutes and period of the day (am/pm).

Return value
An array containing the hour, minutes and period.

'my_time_field' => array(
	'type'       => 'time',
	'label'      => __( 'Time', 'fl-builder' ),
	'default'		=>array(
		'hours'			=> '01',
		'minutes'		=> '00',
		'day_period'	=> 'am'
	)			
),

Once your module is saved, you can access the values:

$settings->my_time_field['hour'];
$settings->my_time_field['minutes'];
$settings->my_time_field['day_period'];

Time Zone

The time zone field displays an input for selecting a specific time zone.

Return value
The time zone string.

'my_timezone_field' => array(
	'type'          => 'timezone',
	'label'         => __( 'Time Zone', 'fl-builder' ),
	'default'		=> 'UTC',
),

Video

The video field displays a WordPress media lightbox that allows users to select a single video.

Return value
The attachment id for the selected video.

'my_video_field' => array(
    'type'          => 'video',
    'label'         => __( 'Video Field', 'fl-builder' )
),

Repeater fields reference

A repeater field is a field that can be duplicated multiple times, reordered and deleted. For a live example of this, check out the Items tab of the Accordion module. There you will see that when you click the Add Item button, another item field is added. This works for any field type, including form fields (as in the Accordion module). The only field types it currently doesn't work with is Editor Fields, Loop Settings Fields, Photo Fields, and Service Fields.

Return value
An array of values for each of the repeated fields.

Usage
Creating a repeater field is simple. Just set the multiple value in the field config array to true and your field will automatically become a repeater.

'my_text_field' => array(
    'type'          => 'text',
    'label'         => __( 'My Text Field', 'fl-builder' ),
    'multiple'      => true,
),

Creating custom fields

In addition to creating custom modules, developers can also define custom fields using their own HTML within a module's settings form.

Custom field rendering function

First, create a function that renders the HTML for your field:

function fl_my_custom_field($name, $value, $field, $settings) {
    echo '<input type="text" class="text text-full" name="' . $name . '" value="' . $value . '" />';
}

This function will be passed the following variables to use within your field's HTML.

$name string
The name of your field that will be stored in the module's settings object.

$value string
The value of your field as stored in the module's settings object.

$field array
The field config array created when defining your custom module settings. The values in this array can be anything you want but take care not to use core builder values such as "type", "label", "default", "help", "description", and "class".

$settings object
The settings for the form that this field belongs to.

Register your rendering function

Once you have defined your field's rendering function, you need to register it using an action as in the example below. The action name should be fl_builder_control_ followed by your field's type. In this example the type is my-custom-field.

add_action('fl_builder_control_my-custom-field', 'fl_my_custom_field', 1, 4);

Once you have defined your custom field, it can be used in your module settings like so...

'example_custom_field' => array(
    'type'          => 'my-custom-field',
    'label'         => __( 'Example Custom Field', 'fl-builder' ),
    'default'       => '',
    'foo'           => 'bar'       
),

Note: Developers should always namespace their custom field types to avoid conflicts with core builder field types. For example, the builder doesn't currently have a field type named number, but if one is added at some point in the future, it will override yours.

Enqueuing custom field assets

Developers may also wish to enqueue CSS and JavaScript assets for their fields while the builder is active. This is done in the same way you typically enqueue assets, except that the enqueue calls are wrapped in an if statement that checks to see if the builder is active:

function fl_my_custom_field_assets() {
    if ( class_exists( 'FLBuilderModel' ) && FLBuilderModel::is_builder_active() ) {
        wp_enqueue_style( 'my-custom-fields', FL_MODULE_EXAMPLES_URL . 'assets/css/fields.css', array(), '' );
        wp_enqueue_script( 'my-custom-fields', FL_MODULE_EXAMPLES_URL . 'assets/js/fields.js', array(), '', true );
    }
}
add_action( 'wp_enqueue_scripts', 'fl_my_custom_field_assets' );

Live preview reference

By default, whenever a user changes a module setting, the builder's layout will refresh to reflect that change. The default refresh functionality works in many cases but can be overridden as detailed in the examples below for an instant preview that doesn't require a refresh.

Additionally, when developing a module, you must write it in a way that you account for the default settings as modules are rendered as soon as they are dropped into a layout. As such, your code should never expect that it's dealing with user submitted settings as it could be dealing with the default settings.

Text

A text preview will insert the text of a field into the element specified by the selector value. The selector value for all preview types is scoped to your module and only needs to include the selector of the element you wish to modify.

'my_text' => array(
    'type'          => 'text',
    'label'         => __('My Text', 'fl-builder'),
    'preview'       => array(
        'type'          => 'text',
        'selector'      => '.fl-example-text'  
    )
),

CSS preview

A CSS preview will use the value of a field combined with the property value to change the style of the element specified by the selector value. A unit value (as shown below) must also be specified for style properties such as font-size or line-height.

'my_font_size' => array(
    'type'          => 'text',
    'label'         => __('My Font Size', 'fl-builder'),
    'description'   => 'px',
    'preview'       => array(
        'type'          => 'css',
        'selector'      => '.fl-example-text',
        'property'      => 'font-size',
        'unit'          => 'px'
    )
),

You can also define an array of rules for your CSS previews that allows a field to affect more than one selector and property.

'my_color' => array(
    'type'          => 'color',
    'label'         => __('My Color', 'fl-builder'),
    'preview'       => array(
        'type'          => 'css',
        'rules'           => array(
            array(
                'selector'     => '.selector-1',
                'property'     => 'color'
            ),
            array(
                'selector'     => '.selector-2',
                'property'     => 'background-color'
            ),    
        )
    )
),

CSS color preview

A CSS color preview is similar to the standard CSS preview except that it only applies to color picker fields. The value of the color picker field will be combined with the property value to change the style of the element specified by the selector value.

'my_color' => array(
    'type'         => 'color',
    'label'        => __('My Color', 'fl-builder'),
    'preview'      => array(
        'type'         => 'css',
        'selector'     => '.fl-example-text',
        'property'     => 'color'
    )
),

None

A preview type of none will prevent any preview from occurring when a module setting field is changed. This preview type can be useful if you wish to code your own custom preview functionality in the module's settings.js file.

'my_select' => array(
    'type'          => 'select',
    'label'         => __('My Select', 'fl-builder'),
    'default'       => 'option-2',
    'options'       => array(
        'option-1'      => __('Option 1', 'fl-builder'),
        'option-2'      => __('Option 2', 'fl-builder')
    ),
    'preview'      => array(
        'type'         => 'none'
    )
),

JavaScript events

There are two JavaScript events available to you when working with live previews that may come in handy:

fl-builder.layout-rendered
This event fires on the main builder content wrapper .fl-builder-content every time the layout is rendered or rerendered, typically when saving settings. It can be used to run additional logic once the render has been completed.

Example:

$( '.fl-builder-content' ).on( 'fl-builder.layout-rendered', myCallbackFunction );

fl-builder.preview-rendered
This event fires on the main builder content wrapper .fl-builder-content every time the layout preview is rendered or rerendered when editing settings. It can be used to run additional logic once the render has been completed.

Example:

$( '.fl-builder-content' ).on( 'fl-builder.preview-rendered', myCallbackFunction );

Partial refresh reference

Current versions of Beaver Builder plugin employ Partial Refresh for layout, in which only the row, column, or module that you are currently editing is updated when you click Save (or a preview refresh is triggered). However, custom module developers need to enable this for their modules and be sure that they are compatible for partial refresh to work. Custom modules that do not support partial refresh will trigger a full layout refresh when edited.

Partial refresh compatibility

Note: If your module doesn't use JavaScript, you can skip this section and enable partial refresh.

To make your custom modules compatible with partial refresh, any JavaScript written for them must only run for the current instance that is being edited. If it doesn't, then when your module is refreshed, the JavaScript will run for all instances on the page (if any are present) potentially causing things to break.

Here's an example of JavaScript code that is NOT partial-refresh compatible and runs for all instances on the page.

$( '.my-module-class' ).each( function(){
	// Do something to each module on the page.	
} );

As you can see, that code will run the same function for each module on the page, even when just a single module is updated.

Here's an example of JavaScript code that is partial-refresh compatible and only runs for an individual instance. Note: this should be in frontend.js.php.

$( '.fl-node-<?php echo $id; ?>' ).css( 'background', '#ffffff' );

That code will only run for a module with a specific ID.

At this point, you may be wondering how you can accomplish this without having to use frontend.js.php for everything. We typically do that by using frontend.js.php for the instance code and frontend.js to house the bulk of the logic. Here's an example:

In frontend.js.php, we instantiate the module's JavaScript logic for the individual instance. Any number of parameters can be passed here. We're just using id for this module.

(function($) {
	$(function() {
		new FLBuilderAccordion({
			id: '<?php echo $id ?>'
		});
	});
})(jQuery);

In frontend.js we build the FLBuilderAccordion object, which has all of the module's JavaScript logic and uses the parameters passed in <frontend.js.php (some code omitted for brevity).

(function($) {

	FLBuilderAccordion = function( settings ) {
		this.settings 	= settings;
		this.nodeClass  = '.fl-node-' + settings.id;
		this._init();
	};

	FLBuilderAccordion.prototype = {
	
		_init: function() {
			$( this.nodeClass + ' .fl-accordion-button' ).click( $.proxy( this._buttonClick, this ) );
		},
		
		_buttonClick: function( e ) {
			...
		},
		
		_slideUpComplete: function() {
			...
		},
		
		_slideDownComplete: function() {
			...
		}
	};
	
})(jQuery);

Enabling partial refresh

Once your module is compatible, enabling partial refresh is easy. Just set the partial_refresh parameter in your module's constructor to true as shown in the example below and partial refresh will be enabled for your module.

class MyModuleClass extends FLBuilderModule {

    public function __construct()
    {
        parent::__construct(array(
            'partial_refresh' => true // Set this to true to enable partial refresh.
        ));
    }
}

Overriding built-In modules

Any of the built-in modules can be overridden within your theme by following the steps below.

1. Create a new folder in your theme's folder named fl-builder.

2. Create a new folder within your theme's fl-builder folder named modules.

3. Copy the module you wish to override from wp-content/plugins/bb-plugin/modules to your theme's fl-builder/modules folder.

That's it! You can now start editing the module to suit your needs. Note that even though you can customize the module's code, the module's folder name, main PHP file name, and main class name must remain unchanged to be recognized by the builder.

If you’re not seeing your changes, try clearing the Page Builder cache.

Localization

You will notice that in all of the examples we are using WordPress' built-in translation functions such as __() and _e(). The default text domain that we have provided, fl-builder, should be changed out to your plugin folder's name should you decide to translate your plugin. See the WordPress codex for more info on translating your plugin.