Beaver Builder uses a custom AJAX system that runs on the frontend instead of wp-admin. This allows proper rendering of shortcodes and widgets while maintaining security through nonces and user capability checks.
FLBuilderAJAX Class
The FLBuilderAJAX class handles all frontend AJAX operations for the builder interface. Unlike wp_ajax, it runs in the frontend context to properly render content.
From class-fl-builder-ajax.php:3-10: Frontend AJAX is used because wp_ajax only works in the admin and certain things like some shortcodes won’t render there. AJAX requests only run for logged-in users for extra security.
How It Works
Action Registration
Actions are registered with FLBuilderAJAX::add_action() during initialization.
Request Handling
The run() method processes requests on the wp hook.
Security Checks
Requests are verified for logged-in users, valid nonces, and edit capabilities.
Action Execution
The specified method is called with extracted arguments.
Registering AJAX Actions
Actions are registered in the add_actions() method (class-fl-builder-ajax.php:82-157):
self :: add_action (
'action_name' , // AJAX action identifier
'ClassName::method' , // Method to call
array ( 'arg1' , 'arg2' ) // Arguments from POST data
);
Example: Built-in Actions
From class-fl-builder-ajax.php:85-109:
// Get node settings
self :: add_action (
'get_node_settings' ,
'FLBuilderModel::get_node_settings' ,
array ( 'node_id' )
);
// Delete node
self :: add_action (
'delete_node' ,
'FLBuilderModel::delete_node' ,
array ( 'node_id' )
);
// Save settings
self :: add_action (
'save_settings' ,
'FLBuilderModel::save_settings' ,
array ( 'node_id' , 'settings' )
);
// Save layout
self :: add_action (
'save_layout' ,
'FLBuilderModel::save_layout' ,
array ( 'publish' , 'exit' )
);
Creating Custom AJAX Actions
For custom modules, use wp_ajax instead of FLBuilderAJAX (class-fl-builder-ajax.php:8-9). FLBuilderAJAX is for core builder operations only.
For core builder extensions, you can register custom actions:
/**
* Add custom AJAX action
*/
function my_add_custom_ajax_action () {
FLBuilderAJAX :: add_action (
'my_custom_action' ,
'MyClass::handle_custom_action' ,
array ( 'custom_param' )
);
}
add_action ( 'fl_ajax_before_call_action' , 'my_add_custom_ajax_action' );
/**
* Handle custom AJAX action
*/
class MyClass {
public static function handle_custom_action ( $custom_param ) {
// Your logic here
$result = array (
'success' => true ,
'data' => $custom_param
);
return $result ;
}
}
Making AJAX Requests
From the builder JavaScript:
// Make AJAX request
FLBuilder . ajax ({
action: 'my_custom_action' ,
custom_param: 'value'
}, function ( response ) {
// Handle response
if ( response . success ) {
console . log ( response . data );
}
});
AJAX Security
The call_action() method (class-fl-builder-ajax.php:166-265) implements multiple security layers:
1. User Authentication
From class-fl-builder-ajax.php:167-170:
// Only run for logged in users.
if ( ! is_user_logged_in () ) {
return ;
}
2. Nonce Verification
From class-fl-builder-ajax.php:172-175:
// Verify the AJAX nonce.
if ( ! self :: verify_nonce () ) {
return ;
}
The nonce verification (class-fl-builder-ajax.php:274-289):
static private function verify_nonce () {
$post_data = FLBuilderModel :: get_post_data ();
$nonce = false ;
if ( isset ( $post_data [ '_wpnonce' ] ) ) {
$nonce = $post_data [ '_wpnonce' ];
} elseif ( isset ( $_REQUEST [ '_wpnonce' ] ) ) {
$nonce = $_REQUEST [ '_wpnonce' ];
}
if ( ! $nonce || ! wp_verify_nonce ( $nonce , 'fl_ajax_update' ) ) {
return false ;
}
return true ;
}
3. Capability Check
From class-fl-builder-ajax.php:189-191:
// Make sure the user can edit this post.
if ( ! current_user_can ( 'edit_post' , $post_id ) ) {
return ;
}
Sending Nonces
Include the nonce in your AJAX requests:
FLBuilder . ajax ({
action: 'my_action' ,
_wpnonce: FLBuilderConfig . ajaxNonce
}, callback );
AJAX Hooks
The AJAX system provides several hooks for customization:
Before Action Hook
From class-fl-builder-ajax.php:202-206:
/**
* Allow developers to modify actions before they are called.
* @see fl_ajax_before_call_action
*/
do_action ( 'fl_ajax_before_call_action' , $action );
Example usage:
function my_before_ajax_action ( $action ) {
// Log AJAX actions
error_log ( 'AJAX Action: ' . $action );
}
add_action ( 'fl_ajax_before_call_action' , 'my_before_ajax_action' );
Before Specific Action
From class-fl-builder-ajax.php:229-234:
/**
* Allow developers to hook before the action runs.
* @see fl_ajax_before_{$action}
*/
do_action ( 'fl_ajax_before_' . $action [ 'action' ], $keys_args );
Example:
function my_before_save_settings ( $args ) {
// Validate settings before saving
$node_id = $args [ 'node_id' ];
$settings = $args [ 'settings' ];
// Your validation logic
}
add_action ( 'fl_ajax_before_save_settings' , 'my_before_save_settings' );
Filter Action Result
From class-fl-builder-ajax.php:236-240:
/**
* Call the action and allow developers to filter the result.
* @see fl_ajax_{$action}
*/
$result = apply_filters (
'fl_ajax_' . $action [ 'action' ],
call_user_func_array ( $action [ 'method' ], $args ),
$keys_args
);
Example:
function my_filter_save_settings ( $result , $args ) {
// Modify the result
$result [ 'custom_data' ] = 'value' ;
return $result ;
}
add_filter ( 'fl_ajax_save_settings' , 'my_filter_save_settings' , 10 , 2 );
After Action Hook
From class-fl-builder-ajax.php:242-246:
/**
* Allow developers to hook after the action runs.
* @see fl_ajax_after_{$action}
*/
do_action ( 'fl_ajax_after_' . $action [ 'action' ], $keys_args );
FLBuilderAJAXLayout Class
The FLBuilderAJAXLayout class handles rendering of layout components for AJAX refreshes.
Rendering Methods
From class-fl-builder-ajax-layout.php:
render() - Main Render Method
Renders layout or node HTML, scripts, and styles (class-fl-builder-ajax-layout.php:27-78): /**
* Renders the layout data to be passed back to the builder.
*
* @param string $node_id The ID of a node to render
* @param string $old_node_id The ID of a node that has been replaced
* @return array
*/
static public function render ( $node_id = null , $old_node_id = null ) {
// Register scripts for shortcodes/widgets
self :: register_scripts ();
// Dequeue to capture only needed scripts
self :: dequeue_scripts_and_styles ();
// Get partial refresh data
$partial_refresh_data = self :: get_partial_refresh_data ();
// Render HTML
$html = self :: render_html ();
// Render scripts and styles
$scripts_styles = self :: render_scripts_and_styles ();
// Render assets (CSS/JS)
$assets = self :: render_assets ();
return array (
'partial' => $partial_refresh_data [ 'is_partial_refresh' ],
'nodeId' => $partial_refresh_data [ 'node_id' ],
'nodeType' => $partial_refresh_data [ 'node_type' ],
'moduleType' => $partial_refresh_data [ 'module_type' ],
'oldNodeId' => $old_node_id ,
'html' => $html ,
'scriptsStyles' => $scripts_styles ,
'css' => $assets [ 'css' ],
'js' => $assets [ 'js' ],
);
}
render_new_row() - Create New Row
From class-fl-builder-ajax-layout.php:89-140: static public function render_new_row (
$cols = '1-col' ,
$position = false ,
$module = null
) {
// Add the row
$row = FLBuilderModel :: add_row ( $cols , $position , $module );
// Render HTML
ob_start ();
FLBuilder :: render_row ( $row );
$html = ob_get_clean ();
// Get new nodes
$new_nodes = FLBuilderModel :: get_nested_nodes ( $row -> node );
$new_nodes [ $row -> node ] = $row ;
// Get updated sibling positions
$siblings = FLBuilderModel :: get_nodes ( 'row' );
$updated_nodes = array ();
foreach ( $siblings as $sibling ) {
if ( $sibling -> node !== $row -> node ) {
$updated_nodes [ $sibling -> node ] = new StdClass ();
$updated_nodes [ $sibling -> node ] -> position = $sibling -> position ;
}
}
return array (
'partial' => true ,
'nodeType' => $row -> type ,
'html' => $html ,
'js' => 'FLBuilder._renderLayoutComplete();' ,
'newNodes' => $new_nodes ,
'updatedNodes' => $updated_nodes ,
);
}
render_new_module() - Create New Module
From class-fl-builder-ajax-layout.php:460-557: static public function render_new_module (
$parent_id ,
$position = false ,
$type = null ,
$alias = null ,
$template_id = null ,
$template_type = 'user'
) {
// Add module from template or default
if ( null !== $template_id ) {
$module = FLBuilderModel :: apply_node_template (
$template_id ,
$parent_id ,
$position
);
} else {
$alias = FLBuilderModel :: get_module_alias ( $alias );
if ( $alias ) {
$module = FLBuilderModel :: add_default_module (
$parent_id ,
$type ,
$position ,
$alias -> settings ,
$alias -> template
);
} else {
$module = FLBuilderModel :: add_default_module (
$parent_id ,
$type ,
$position
);
}
}
// Determine render strategy (partial or full refresh)
if ( $module -> partial_refresh ) {
// Partial refresh logic...
}
return array (
'type' => $module -> settings -> type ,
'nodeId' => $module -> node ,
'parentId' => $module -> parent ,
'dynamic' => FLBuilderModel :: is_node_dynamic ( $module ),
'global' => FLBuilderModel :: is_node_global ( $module ),
'layout' => self :: render ( $render_id ),
'settings' => $module -> settings ,
'newNodes' => $new_nodes ,
'updatedNodes' => $updated_nodes ,
);
}
copy_module() - Duplicate Module
From class-fl-builder-ajax-layout.php:567-613: static public function copy_module ( $node_id , $settings = null ) {
// Copy the module
$module = FLBuilderModel :: copy_module ( $node_id , $settings );
// Render the copied module
$response = self :: render ( $module -> node );
// Get new nodes
$children = FLBuilderModel :: get_nested_nodes ( $module -> node );
$new_nodes = FLBuilderModel :: clean_layout_data (
array_merge ( array ( $module -> node => $module ), $children )
);
// Get updated sibling positions
$siblings = FLBuilderModel :: get_nodes ( null , $module -> parent );
$updated_nodes = array ();
foreach ( $siblings as $sibling ) {
if ( $sibling -> node !== $module -> node ) {
$updated_nodes [ $sibling -> node ] = new StdClass ();
$updated_nodes [ $sibling -> node ] -> position = $sibling -> position ;
}
}
return array_merge ( $response , array (
'newNodes' => $new_nodes ,
'updatedNodes' => $updated_nodes ,
));
}
Partial Refresh System
The partial refresh system (class-fl-builder-ajax-layout.php:648-695) determines if only a specific node needs re-rendering:
static private function get_partial_refresh_data () {
$post_data = FLBuilderModel :: get_post_data ();
$partial_refresh = false ;
if ( isset ( $post_data [ 'node_id' ] ) ) {
$node = FLBuilderModel :: get_node ( $post_data [ 'node_id' ] );
if ( $node && 'module' == $node -> type ) {
$node = FLBuilderModel :: get_module ( $node_id );
if ( is_object ( $node ) ) {
$partial_refresh = $node -> partial_refresh ;
}
}
}
return array (
'is_partial_refresh' => $partial_refresh ,
'node_id' => $node_id ,
'node' => $node ,
'node_type' => $node_type ,
'module_type' => $module_type ,
);
}
Custom Module AJAX
For custom modules, use WordPress’s standard wp_ajax hooks:
/**
* Register AJAX actions for custom module
*/
function my_module_register_ajax () {
// For logged-in users
add_action ( 'wp_ajax_my_module_action' , 'my_module_ajax_handler' );
// For non-logged-in users (if needed)
add_action ( 'wp_ajax_nopriv_my_module_action' , 'my_module_ajax_handler' );
}
add_action ( 'init' , 'my_module_register_ajax' );
/**
* Handle AJAX request
*/
function my_module_ajax_handler () {
// Verify nonce
check_ajax_referer ( 'my-module-nonce' , 'nonce' );
// Get parameters
$param = isset ( $_POST [ 'param' ] ) ? sanitize_text_field ( $_POST [ 'param' ] ) : '' ;
// Process request
$result = array (
'success' => true ,
'data' => 'Result: ' . $param
);
// Return JSON response
wp_send_json_success ( $result );
}
Making the request from your module:
( function ( $ ) {
var MyModule = {
init : function () {
$ ( '.my-button' ). on ( 'click' , this . handleClick );
},
handleClick : function () {
$ . ajax ({
url: FLBuilderLayoutConfig . paths . wpAjaxUrl ,
type: 'POST' ,
data: {
action: 'my_module_action' ,
nonce: 'my-module-nonce-value' ,
param: 'value'
},
success : function ( response ) {
if ( response . success ) {
console . log ( response . data );
}
}
});
}
};
$ ( document ). ready ( function () {
MyModule . init ();
});
})( jQuery );
Best Practices
Security First Always verify nonces and check user capabilities before processing AJAX requests.
Use wp_ajax for Modules Use standard WordPress AJAX for custom modules, not FLBuilderAJAX.
Return Structured Data Return consistent array structures with success/error states.
Handle Errors Implement proper error handling and return meaningful error messages.
Data Model Learn about layout data structure
CSS & JS Assets Asset generation and caching
Custom Modules Creating custom modules
Hooks Reference Available hooks and filters