Script Data
Introduction
Script Data is a critical mechanism in Divi 5 that bridges PHP server-side rendering and JavaScript frontend execution. It enables modules to pass configuration data from the server to client-side scripts, allowing interactive features like sticky positioning, scroll effects, animations, and more to initialize properly. Without Script Data registration, these features won't work because JavaScript needs module-specific configuration that's only available during server-side rendering.
This guide explains what Script Data is, why modules need it, and how to properly implement it using both ModuleScriptDataTrait (PHP) and ModuleScriptDataProps (React). Understanding Script Data is essential for creating modules with interactive features.
What is Script Data?
Script Data is a data passing mechanism that allows PHP modules to send configuration information to JavaScript scripts running in the browser. It uses WordPress's wp_localize_script() function under the hood to enqueue module-specific data that JavaScript can access.
When a module renders on the server side, it can register Script Data containing information like:
- Module ID and CSS selector
- Decoration attributes (sticky, scroll effects, animations, interactions, links)
- Background parallax and video settings
- Multi-view responsive content configuration
This data is then made available to JavaScript scripts on the frontend, enabling them to initialize interactive features with the correct module-specific settings.
Why Modules Need Script Data
Modules need Script Data for any feature that requires JavaScript initialization. Without Script Data registration, these features won't initialize because JavaScript doesn't have access to the module's configuration.
The following features require Script Data:
- Sticky Positioning: Modules with sticky options need Script Data to tell JavaScript which elements should stick and when
- Scroll Effects: Scroll-triggered animations and transformations require Script Data to know what to animate
- Animations: Entrance animations and transitions need Script Data to initialize properly
- Interactions: Hover, click, and other interaction effects need Script Data for configuration
- Links: Custom link behaviors may require Script Data for proper initialization
- Background Parallax/Video: Background effects need Script Data to know which elements to target
- Multi-view: Responsive content switching requires Script Data to manage breakpoint-specific content
If your module uses any of these features, you must implement Script Data registration. Even if your module doesn't currently use these features, implementing basic Script Data ensures future compatibility and proper Visual Builder preview functionality.
How Script Data Works
Script Data requires implementation on both the PHP (server-side) and React (Visual Builder) sides. The PHP side registers the data during server-side rendering, while the React side ensures the Visual Builder can preview and interact with these features.
PHP Side: ModuleScriptDataTrait
On the PHP side, Script Data is registered through the ModuleScriptDataTrait::module_script_data() method. This method receives module attributes and calls ElementScriptData::set() to register script data for various features.
The PHP implementation:
- Receives module ID, selector, attributes, and other context
- Calls
ElementScriptData::set()with decoration attributes - Optionally calls specialized script data classes like
MultiViewScriptData::set()for advanced features - Data is stored in the
ScriptDataclass and enqueued viawp_localize_script()
React Side: ModuleScriptData Component
On the React side, Script Data is registered through a ModuleScriptData component that receives ModuleScriptDataProps. This component calls elements.scriptData() to register script data in the Visual Builder.
The React implementation:
- Receives
id,attrs,selector, andelementsas props (all optional exceptattrsis typically provided) - Calls
elements.scriptData({ attrName: 'module' })to register script data - Can also use
useElementScriptData()hook directly for custom implementations - Ensures Visual Builder can preview and interact with script-dependent features
Both sides must be implemented for features to work correctly. The PHP side handles frontend rendering, while the React side handles Visual Builder preview.
The Connection Between PHP and React
The module_script_data() method in your PHP trait is called during server-side rendering to register script data for the frontend. The React ModuleScriptData component (passed as scriptDataComponent to ModuleContainer) performs the same registration in the Visual Builder, ensuring features work correctly in both contexts.
When you pass scriptDataComponent={ModuleScriptData} to ModuleContainer, you're telling Divi to use that React component to register script data in the Visual Builder, mirroring what module_script_data() does on the server side. This dual implementation ensures that:
- Frontend JavaScript receives the necessary configuration data (via PHP)
- Visual Builder can preview and interact with script-dependent features (via React)
- Both environments have consistent script data registration
Basic Usage
The most common Script Data implementation is minimal, registering only the module's decoration attributes. This covers most standard features like sticky, scroll effects, animations, and interactions.
PHP Implementation
Here's a basic PHP implementation using ModuleScriptDataTrait. There are two equivalent ways to register script data:
Method 1: Using $elements->script_data() (Simpler pattern)
This is the simplest pattern that works for most modules:
<?php
namespace MyNamespace\Modules\MyModule\MyModuleTrait;
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
/**
* ModuleScriptDataTrait trait.
*
* Provides script data registration functionality for modules.
* This trait handles registration of script data required for interactive
* features like sticky positioning, scroll effects, animations, and more.
*/
trait ModuleScriptDataTrait {
/**
* Set script data of used module options.
*
* @param array $args {
* @type string $id Module id.
* @type string $name Module name.
* @type string $selector Module selector.
* @type array $attrs Module attributes.
* @type object $elements Module elements instance.
* @type mixed $storeInstance Optional. Store instance for frontend rendering.
* }
*/
public static function module_script_data( $args ) {
$elements = $args['elements'];
// Element Script Data Options.
$elements->script_data(
[
'attrName' => 'module',
]
);
}
}
This pattern automatically registers script data for all decoration features (sticky, scroll effects, animations, interactions, links, background effects) based on the module's attributes. It's the recommended approach for most modules.
Method 2: Using ElementScriptData::set() (More explicit control)
For modules that need more explicit control or want to customize which attributes are registered:
<?php
namespace MyNamespace\Modules\MyModule\MyModuleTrait;
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
use ET\Builder\Packages\Module\Options\Element\ElementScriptData;
/**
* ModuleScriptDataTrait trait.
*
* Provides script data registration functionality for modules.
* This trait handles registration of script data required for interactive
* features like sticky positioning, scroll effects, animations, and more.
*/
trait ModuleScriptDataTrait {
/**
* Set script data of used module options.
*
* @param array $args {
* @type string $id Module id.
* @type string $name Module name.
* @type string $selector Module selector.
* @type array $attrs Module attributes.
* @type object $elements Module elements instance.
* @type mixed $storeInstance Optional. Store instance for frontend rendering.
* }
*/
public static function module_script_data( $args ) {
$id = $args['id'] ?? '';
$selector = $args['selector'] ?? '';
$attrs = $args['attrs'] ?? [];
$store_instance = $args['storeInstance'] ?? null;
// Module decoration attributes.
$module_decoration_attrs = $attrs['module']['decoration'] ?? [];
// Element Script Data Options.
ElementScriptData::set(
[
'id' => $id,
'selector' => $selector,
'attrs' => array_merge(
$module_decoration_attrs,
[
'link' => $args['attrs']['module']['advanced']['link'] ?? [],
]
),
'storeInstance' => $store_instance,
]
);
}
}
Both methods achieve the same result. Use Method 1 for simplicity, or Method 2 when you need explicit control over which attributes are registered.
React Implementation
The React side uses elements.scriptData() to register script data in the Visual Builder. This is the recommended approach for most modules, as useElementScriptData hook is used internally by elements.scriptData().
Basic Usage: Single Element
The most common pattern registers script data for the module element:
import React, { Fragment, type ReactElement } from 'react';
import { type ModuleScriptDataProps } from '@divi/module';
import { type MyModuleAttrs } from '@divi/types';
/**
* My module's script data component.
*
* @returns {ReactElement}
*/
export const ModuleScriptData = ({ elements }: ModuleScriptDataProps<MyModuleAttrs>): ReactElement => (
<Fragment>
{elements.scriptData({
attrName: 'module',
})}
</Fragment>
);
This minimal implementation registers script data for the module element. The elements.scriptData() method automatically handles registration based on the module's decoration attributes.
Multiple Elements
For modules with multiple elements that need script data registration, call elements.scriptData() multiple times:
import React, { Fragment, type ReactElement } from 'react';
import { type ModuleScriptDataProps } from '@divi/module';
import { type MyModuleAttrs } from '@divi/types';
/**
* My module's script data component.
*
* @returns {ReactElement}
*/
export const ModuleScriptData = ({ elements }: ModuleScriptDataProps<MyModuleAttrs>): ReactElement => (
<Fragment>
{elements.scriptData({
attrName: 'module',
})}
{elements.scriptData({
attrName: 'title',
})}
{elements.scriptData({
attrName: 'button',
})}
</Fragment>
);
Using attrsResolver to Modify Attributes
The attrsResolver parameter allows you to modify or filter decoration attributes before they're registered. This is useful when you need to transform attributes or exclude certain properties:
import React, { Fragment, type ReactElement } from 'react';
import { type ModuleScriptDataProps } from '@divi/module';
import { type Module } from '@divi/types';
import { type MyModuleAttrs } from '@divi/types';
/**
* My module's script data component.
*
* @returns {ReactElement}
*/
export const ModuleScriptData = ({ elements }: ModuleScriptDataProps<MyModuleAttrs>): ReactElement => (
<Fragment>
{elements.scriptData({
attrName: 'module',
attrsResolver: (attrs: Module.Element.Decoration.Attributes) => {
// Modify attributes before registration.
// For example, exclude certain properties or merge additional data.
const { scroll, sticky, ...rest } = attrs;
return {
...rest,
scroll: scroll ? { ...scroll, customProperty: 'value' } : undefined,
};
},
})}
</Fragment>
);
Using scriptDataProps for Custom Configuration
The scriptDataProps parameter allows you to override default script data properties, such as custom selectors or additional configuration:
import React, { Fragment, type ReactElement } from 'react';
import { type ModuleScriptDataProps } from '@divi/module';
import { type MyModuleAttrs } from '@divi/types';
/**
* My module's script data component.
*
* @returns {ReactElement}
*/
export const ModuleScriptData = ({ elements, selector }: ModuleScriptDataProps<MyModuleAttrs>): ReactElement => (
<Fragment>
{elements.scriptData({
attrName: 'module',
scriptDataProps: {
selector: `${selector} .custom-wrapper`,
scroll: {
selector: `${selector} .scroll-target`,
},
},
})}
</Fragment>
);
Registering the Component
The ModuleScriptData component must be passed to the ModuleContainer in your module's edit component:
import { ModuleScriptData } from './module-script-data';
const { ModuleContainer } = window?.divi?.module;
export const MyModuleEdit = ({ attrs, elements, id, name }) => {
return (
<ModuleContainer
attrs={attrs}
elements={elements}
id={id}
name={name}
scriptDataComponent={ModuleScriptData}
>
{/* Module content */}
</ModuleContainer>
);
};
Advanced Usage
For modules with more complex requirements, you can extend Script Data registration to include additional features like Multi-view responsive content or custom script data handling.
Multi-view Script Data
If your module uses Multi-view for responsive content switching, you'll need to register Multi-view Script Data in addition to Element Script Data. Note that name is required for Multi-view Script Data registration, and hoverSelector is optional (defaults to the module selector if not provided).
use ET\Builder\Packages\Module\Layout\Components\MultiView\MultiViewScriptData;
use ET\Builder\Packages\Module\Options\Element\ElementScriptData;
/**
* ModuleScriptDataTrait trait.
*
* Provides script data registration functionality for modules.
* This trait handles registration of script data required for interactive
* features like sticky positioning, scroll effects, animations, and more.
* Includes Multi-view Script Data registration for responsive content switching.
*/
trait ModuleScriptDataTrait {
/**
* Set script data of used module options.
*
* @param array $args {
* @type string $id Module id.
* @type string $name Module name.
* @type string $selector Module selector.
* @type array $attrs Module attributes.
* @type object $elements Module elements instance.
* @type mixed $storeInstance Optional. Store instance for frontend rendering.
* }
*/
public static function module_script_data( $args ) {
$id = $args['id'] ?? '';
$name = $args['name'] ?? '';
$selector = $args['selector'] ?? '';
$attrs = $args['attrs'] ?? [];
$store_instance = $args['storeInstance'] ?? null;
$module_decoration_attrs = $attrs['module']['decoration'] ?? [];
// Element Script Data Options.
ElementScriptData::set(
[
'id' => $id,
'selector' => $selector,
'attrs' => array_merge(
$module_decoration_attrs,
[
'link' => $args['attrs']['module']['advanced']['link'] ?? [],
]
),
'storeInstance' => $store_instance,
]
);
// Multi-view Script Data for responsive content.
MultiViewScriptData::set(
[
'id' => $id,
'name' => $name,
'hoverSelector' => $selector,
'setContent' => [
[
'selector' => $selector . ' .my-module__title',
'data' => $attrs['title']['innerContent'] ?? [],
'valueResolver' => function( $value ) {
return $value ?? '';
},
],
[
'selector' => $selector . ' .my-module__content',
'data' => $attrs['content']['innerContent'] ?? [],
'valueResolver' => function( $value ) {
return $value ?? '';
},
'sanitizer' => 'et_core_esc_previously',
],
],
]
);
}
}
Multi-view Script Data allows you to define how content changes across breakpoints, enabling responsive content switching without page reloads.
Advanced: Using useElementScriptData Hook Directly
The useElementScriptData hook is used internally by elements.scriptData(). In most cases, you should use elements.scriptData() instead. However, for special cases where you need direct control over script data registration, you can use useElementScriptData hook directly:
import { type ReactElement } from 'react';
import { type ModuleScriptDataProps, useElementScriptData } from '@divi/module';
import { type Module } from '@divi/types';
import { type MyModuleAttrs } from '@divi/types';
export const ModuleScriptData = ({
elements,
attrs,
selector,
id,
}: ModuleScriptDataProps<MyModuleAttrs>): ReactElement | null => {
// Custom script data registration for special cases.
// This is only needed when elements.scriptData() doesn't meet your requirements.
if (attrs.customFeature) {
const decorationAttrs: Module.Element.Decoration.Attributes = {
scroll: attrs.module?.decoration?.scroll,
sticky: attrs.module?.decoration?.sticky,
};
useElementScriptData({
id,
selector,
attrs: decorationAttrs,
});
}
// Standard script data registration using elements.scriptData().
return elements.scriptData({
attrName: 'module',
});
};
When to use useElementScriptData directly:
- You need to conditionally register script data based on complex logic
- You're working with legacy code or backward compatibility scenarios
- You need to register script data outside of the standard module element flow
Note: elements.scriptData() internally uses useElementScriptData, so prefer elements.scriptData() for standard use cases as it handles attribute resolution, selector interpolation, and other common patterns automatically.
Best Practices
When implementing Script Data, follow these best practices:
- Always implement both sides: Both PHP and React implementations are required for features to work correctly. The PHP side handles frontend rendering, while the React side handles Visual Builder preview.
- Prefer
elements.scriptData()overuseElementScriptData: Useelements.scriptData()for most cases, as it handles common patterns automatically. TheuseElementScriptDatahook is used internally byelements.scriptData()and should only be used for special cases - Use the simpler pattern when possible: For most modules, use
$elements->script_data()(Method 1) as it's simpler and automatically handles all decoration features. - Use ElementScriptData for standard features: The
ElementScriptData::set()method automatically handles most common features (sticky, scroll effects, animations, interactions, links, background effects). - Register Multi-view separately: If using responsive content switching, register Multi-view Script Data in addition to Element Script Data. Remember that
nameis required for Multi-view Script Data. - Keep implementations minimal: Start with basic Script Data registration and only add complexity when needed. Most modules only need the basic pattern.
- Handle empty attributes gracefully: Script data registration will handle empty or missing attributes gracefully, so you don't need to check if attributes exist before registering.
- Reference existing examples: Look at core modules like Text module for basic patterns, and StaticModule example for advanced patterns with Multi-view support.
- Keep implementations minimal: Start with basic Script Data registration (
elements.scriptData({ attrName: 'module' })) and only add complexity when needed - Use
attrsResolverwhen you need to modify attributes: If you need to transform or filter decoration attributes before registration, use theattrsResolverparameter - Reference existing examples: Look at core modules like Text module for basic patterns, Product Meta module for multiple elements, and StaticModule example for advanced patterns
For more information on related topics, see:
- Module Conversion Tutorial - Shows Script Data in context of module conversion
- Element Style Documentation - Explains how styles relate to Script Data
- Responsive Content Documentation - Shows Multi-view Script Data usage
Got a question or feedback? Ask away!