A clean, working example of a custom modal in Divi 5 Visual Builder that demonstrates best practices for third-party developers.
- WrapperContainer setup with drag, resize, expand, and snap capabilities
- Header component with internationalized title
- BodyContainer and PanelContainer structure
- ErrorBoundary integration for crash prevention
- withSelect HOC pattern for connecting to Divi stores
- Accessing clipboard data from
divi/clipboardstore - Props mapping from Redux state to component
- Empty state handling with helpful user guidance
- Conditional rendering patterns
- Professional styling and layout
- Scrollable content for large datasets
- Modern Sass compilation without deprecation warnings
- Clean webpack configuration
- Proper externals for Divi dependencies
- Optimized bundle size
- Download/Clone this plugin to your WordPress plugins directory
- Install dependencies:
npm install - Build the plugin:
npm run build - Activate in WordPress Admin β Plugins
- Open Divi Visual Builder on any page
- Look for "Clipboard" button in the builder toolbar
- Click to open the modal
- Modal shows helpful guidance on how to populate clipboard
- Clean, professional empty state design
- Right-click any module β "Copy" or "Copy Styles"
- Open clipboard modal to see the copied data
- Inspect the structure of clipboard objects
src/
βββ modal/
β βββ component.jsx # Main modal component
β βββ container.jsx # Redux connection (withSelect)
β βββ style.scss # Modal-specific styles
βββ index.jsx # Plugin entry point
βββ add-bar-builder-buttons.js # Toolbar button registration
export const DevClipboard = ({ name, clipboardItems }) => (
<ErrorBoundary>
<WrapperContainer draggable resizable expandable snappable modalName={name}>
<Header name={__('Clipboard', 'et_builder')} />
<BodyContainer>
<PanelContainer id="clipboard" opened>
{/* Content with empty state handling */}
</PanelContainer>
</BodyContainer>
</WrapperContainer>
</ErrorBoundary>
);export default withSelect(selectStore => ({
clipboardItems: selectStore('divi/clipboard').getItems(),
}))(DevClipboard);import { addFilter } from '@wordpress/hooks';
addFilter('divi.modalLibrary.modalMapping', 'divi', modals => ({
...modals,
devClipboard: modal,
}));Replace the clipboard content with your own data:
// In component.jsx
<PanelContainer id="my-panel" opened>
<div style={{ padding: '20px' }}>
<h3>My Custom Content</h3>
<p>Add your own components here...</p>
</div>
</PanelContainer><WrapperContainer multiPanels modalName={name}>
<BodyContainer>
<PanelContainer id="panel1" label="Tab 1" opened>
{/* First tab content */}
</PanelContainer>
<PanelContainer id="panel2" label="Tab 2" opened>
{/* Second tab content */}
</PanelContainer>
</BodyContainer>
</WrapperContainer>// Create your own store and connect it
export default withSelect(selectStore => ({
myData: selectStore('my-plugin/data').getItems(),
}))(MyModalComponent);- Always use ErrorBoundary to prevent modal crashes
- Handle empty states gracefully with helpful messaging
- Use internationalization (
__()) for all user-facing text - Provide helpful comments for complex logic
- Test with empty data before testing with real data
- Single Panel: Use
openedprop, nomultiPanels - Multiple Panels: Use
multiPanelswithlabelprops - Conditional Content: Use
isEmpty()checks for graceful fallbacks - Scrollable Content: Add
overflow: 'auto'for long content
# Development
npm run build
# Production (minified)
NODE_ENV=production npm run build- Check that plugin is activated
- Verify build completed successfully
- Check browser console for JavaScript errors
- Ensure
sass-loaderhasapi: 'modern'option - Update browserslist data:
npx update-browserslist-db@latest
- Check Redux store connection in
container.jsx - Verify
withSelectis properly configured - Check that
modalNameprop is passed correctly
- Divi 5 Modal Components: [Internal docs reference]
- Redux Store Architecture: [Gutenberg Redux patterns]
- WordPress Hooks API: [WordPress Developer Handbook]
This example provides a solid foundation. For more advanced features:
- Field Persistence: See issue #45416 for field components and custom stores
- GroupContainer: Check enhanced examples for expand/collapse patterns
- Complex Redux: Explore builder settings modal for advanced store patterns
Issue: #45415 - Fix clipboard modal navigation and build issues
Status: β
Fixed - Clean, documented, working example
Next: #45416 - Enhanced field persistence examples