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:

  1. Log into the Developer Console and navigate to your application

  2. Create a new Custom AddOn and select "Button" as the type

  3. Configure the addon with a unique handle (e.g., my-button-addon)

  4. Choose your implementation method (Content Dialog or External iframe)

  5. Save the addon configuration

Important: The handle you create in the Developer Console must match exactly with the addon ID you reference in your code's beeConfig. This handle serves as the unique identifier that connects your code implementation to the addon configuration in the console.

Setup in Beefree SDK Console

Field
Value Example

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.

{
  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?