Button AddOn
Overview
The Button AddOn type allows you to insert pre-styled call-to-action buttons into the Beefree editor. This is perfect for providing users with brand-compliant button styles, reducing design work and ensuring consistency across emails.
Prerequisites
Before implementing a Button AddOn in your code, you must first create the addon in the Beefree SDK Developer Console:
Log into the Developer Console and navigate to your application
Create a new Custom AddOn and select "Button" as the type
Configure the addon with a unique handle (e.g.,
my-button-addon
)Choose your implementation method (Content Dialog or External iframe)
Save the addon configuration
Setup in Beefree SDK Console
Name
Simple Button AddOn
Type
Button
← Select from dropdown
Handle
simple-button-addon
Method
Content Dialog
or External iframe
Content Object Schema
Required Structure
The Button AddOn requires a specific data structure to properly insert button content into the editor. The schema defines how the button will appear and behave when added to the email template.
Important: Padding and border-radius values must be numbers (representing pixels), not strings. For example, use 'border-radius': 4
not 'border-radius': '4px'
.
{
type: 'button',
value: {
label: string, // Required: Button text
href: string, // Optional: Link URL
'font-family': string,
'font-size': string,
'background-color': string,
'border-radius': number, // Pixels as number (e.g., 4)
color: string, // Text color
'padding-top': number, // Pixels as number (e.g., 12)
'padding-right': number, // Pixels as number (e.g., 24)
'padding-bottom': number, // Pixels as number (e.g., 12)
'padding-left': number, // Pixels as number (e.g., 24)
width: string,
direction: string // 'ltr' or 'rtl'
}
}
Basic Example
This minimal example demonstrates the simplest button you can create, containing only the required type
and label
properties. This button will use the editor's default styling and can be customized by the user after insertion.
resolve({
type: 'button',
value: {
label: 'Click Here',
href: 'https://example.com'
}
});
Styled Example
This example shows a fully customized button with branded colors, sizing, and padding. By pre-configuring these properties, you ensure consistent brand styling across all buttons without requiring users to manually style each one.
resolve({
type: 'button',
value: {
label: 'Get Started',
href: 'https://example.com/signup',
'font-size': '16px',
'background-color': '#0066CC',
'border-radius': 4, // Number, not string
color: '#FFFFFF',
'padding-top': 12, // Number, not string
'padding-right': 24, // Number, not string
'padding-bottom': 12, // Number, not string
'padding-left': 24 // Number, not string
}
});
Content Dialog Implementation
Basic Handler
The Content Dialog method allows you to programmatically insert button content using a JavaScript handler function. When users drag and drop your button addon onto the stage, the handler is immediately invoked with resolve
and reject
functions that control the insertion flow. This basic pattern immediately resolves with a pre-defined button, making it perfect for simple use cases where no user interaction is needed.
const beeConfig = {
container: 'bee-editor',
// Enable the addon with Direct Open feature
addOns: [
{
id: 'simple-button-addon', // Must match handle from Console
openOnDrop: true
}
],
// Define the handler for button insertion
contentDialog: {
addOn: {
handler: (resolve, reject, args) => {
// Check which addon triggered this handler
if (args.contentDialogId === 'simple-button-addon') {
// Immediately insert a button with predefined values
resolve({
type: 'button',
value: {
label: 'Hello World!',
href: 'https://example.com',
'background-color': '#0066CC',
color: '#FFFFFF',
'border-radius': 4, // Number, not string
'padding-top': 12, // Number, not string
'padding-right': 24, // Number, not string
'padding-bottom': 12, // Number, not string
'padding-left': 24 // Number, not string
}
});
}
}
}
}
};
Pattern: Button Style Library
This advanced pattern demonstrates how to create a library of pre-defined button styles for users to choose from. By opening a custom UI selector, users can pick between different button variants (primary, secondary, etc.) while maintaining brand consistency. The handler waits for user selection before resolving, giving you full control over the insertion flow.
contentDialog: {
addOn: {
handler: (resolve, reject, args) => {
// Define multiple button style presets
const buttonStyles = {
primary: {
'background-color': '#0066CC',
'color': '#FFFFFF',
'border-radius': 4, // Number, not string
'padding-top': 12, // Number, not string
'padding-right': 24, // Number, not string
'padding-bottom': 12, // Number, not string
'padding-left': 24 // Number, not string
},
secondary: {
'background-color': '#6C757D',
'color': '#FFFFFF',
'border-radius': 4, // Number, not string
'padding-top': 12, // Number, not string
'padding-right': 24, // Number, not string
'padding-bottom': 12, // Number, not string
'padding-left': 24 // Number, not string
}
};
// Open your custom UI for style selection
// Replace 'yourButtonSelector' with your actual UI component
yourButtonSelector.open({
styles: buttonStyles,
onSelect: (styleKey, buttonText, buttonUrl) => {
// User confirmed - resolve with selected style
resolve({
type: 'button',
value: {
label: buttonText,
href: buttonUrl,
...buttonStyles[styleKey]
}
});
},
onCancel: () => {
// User canceled - reject to abort insertion
reject();
}
});
}
}
}
Iframe Implementation
Conceptual Flow
The Iframe method allows you to build a completely custom UI for your button addon using any web technology. Your iframe application is loaded inside the Beefree editor and communicates using postMessage
events. This approach gives you complete control over the user experience, letting you create rich interfaces for button customization, validation, and preview before insertion.
Required postMessage Communication
When using the Iframe method, your application must follow a specific message protocol to communicate with the Beefree editor. First, send a "loaded" message to indicate your iframe is ready. Then, listen for the "init" message from Beefree which provides context about the editor state. Finally, send "onSave" with your button data when ready to insert, or "onCancel" if the user abandons the action.
1. Send "loaded" when your iframe is ready:
window.parent.postMessage({
action: 'loaded',
data: {
width: '600px',
height: '400px',
isRounded: true,
hasTitleBar: true,
showTitle: true
}
}, '*');
2. Listen for "init" and "load" messages from Beefree:
window.addEventListener('message', (event) => {
const { action, data } = event.data;
if (action === 'init') {
console.log('Editor locale:', data.locale);
// Initialize your UI with context from Beefree
}
if (action === 'load') {
// Pre-populate when editing existing button
if (data && data.value) {
document.getElementById('buttonText').value = data.value.label || '';
document.getElementById('buttonUrl').value = data.value.href || '';
}
}
});
3. Send "onSave" with button data:
// When user clicks save/insert button
window.parent.postMessage({
action: 'onSave',
data: {
type: 'button',
value: {
label: 'Click Me',
href: 'https://example.com',
'background-color': '#0066CC',
color: '#FFFFFF',
'border-radius': 4, // Number, not string
'padding-top': 12, // Number, not string
'padding-right': 24, // Number, not string
'padding-bottom': 12, // Number, not string
'padding-left': 24 // Number, not string
}
}
}, '*');
4. Send "onCancel" if user cancels:
// When user clicks cancel or closes dialog
window.parent.postMessage({
action: 'onCancel'
}, '*');
Simple Iframe Example
This complete HTML example shows a simple but functional button configuration interface. It demonstrates the full communication protocol with Beefree, including the loaded notification, button configuration inputs, and save/cancel actions. You can use this as a starting point and enhance it with your own styling, validation, and additional configuration options.
<!DOCTYPE html>
<html>
<head>
<title>Button Configurator</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
input, button {
margin: 10px 0;
padding: 8px;
width: 100%;
}
</style>
</head>
<body>
<h2>Configure Button</h2>
<input type="text" id="buttonText" placeholder="Button text" value="Hello World!">
<input type="url" id="buttonUrl" placeholder="Link URL" value="https://example.com">
<input type="color" id="buttonColor" value="#0066CC">
<button onclick="insertButton()">Insert Button</button>
<button onclick="cancel()">Cancel</button>
<script>
// Notify Beefree that iframe is loaded and ready
window.parent.postMessage({
action: 'loaded',
data: {
width: '600px',
height: '400px',
isRounded: true,
hasTitleBar: true,
showTitle: true
}
}, '*');
// Listen for messages from Beefree
window.addEventListener('message', (event) => {
const { action, data } = event.data;
if (action === 'init') {
console.log('Initialized with locale:', data.locale);
}
if (action === 'load' && data?.value) {
// Pre-populate when editing existing button
document.getElementById('buttonText').value = data.value.label || '';
document.getElementById('buttonUrl').value = data.value.href || '';
}
});
function insertButton() {
const text = document.getElementById('buttonText').value;
const url = document.getElementById('buttonUrl').value;
const color = document.getElementById('buttonColor').value;
// Send button data to Beefree
window.parent.postMessage({
action: 'onSave',
data: {
type: 'button',
value: {
label: text,
href: url,
'background-color': color,
color: '#FFFFFF',
'border-radius': 4, // Number, not string
'padding-top': 12, // Number, not string
'padding-right': 24, // Number, not string
'padding-bottom': 12, // Number, not string
'padding-left': 24 // Number, not string
}
}
}, '*');
}
function cancel() {
window.parent.postMessage({ action: 'onCancel' }, '*');
}
</script>
</body>
</html>
Last updated
Was this helpful?