Create a Custom Form Builder in Beefree SDK

Learn how to build a custom form builder interface that integrates with Beefree SDK, allowing end users to create forms dynamically and add them to their email designs.

Why Build a Custom Form Builder?

While pre-defined default forms are great for common use cases, sometimes your end users need complete flexibility to create custom forms from scratch. A form builder interface lets users:

  1. Dynamically add form fields of different types (text, email, select, etc.)

  2. Preview forms as they build them

  3. Customize field properties like labels and options

  4. Save and integrate forms directly into their page and popup designs

  5. Reuse form structures across multiple campaigns

With Beefree SDK's contentDialog.manageForm handler, you can create a modal-based form builder that seamlessly integrates with the editor workflow.

Project Map: Where to Look in the Sample Project

This recipe is based on the beefree-sdk-form-block-demo GitHub project. Clone it, then explore these key files:

File
Purpose
What You'll Learn

Complete form builder implementation

How to create a modal-based form builder with live preview

Authentication proxy server

How to handle secure Beefree SDK authentication

Base email template

How forms integrate with email designs

Project documentation

Understanding the complete form block ecosystem

Note: This form builder uses a modal interface that opens when users click "Edit Form" in the Beefree SDK editor.

Data Flow Diagram

Here's how the custom form builder integrates with Beefree SDK:

+-------------------+        +-------------------+        +-------------------+
|                   |        |                   |        |                   |
| Beefree SDK       | -----> | contentDialog     | -----> | Form Builder      |
| Editor            |        | manageForm        |        | Modal             |
|                   |        |                   |        |                   |
+-------------------+        +-------------------+        +-------------------+
         ^                            |                            |
         |                            |                            |
         |                            v                            v
+-------------------+        +-------------------+        +-------------------+
|                   |        |                   |        |                   |
| Form Integration  | <----- | Form Structure    | <----- | Live Preview      |
| (Drag & Drop)     |        | (JSON)            |        | (Real-time)       |
|                   |        |                   |        |                   |
+-------------------+        +-------------------+        +-------------------+

Why this flow? The SDK triggers the form builder via contentDialog, users build forms with live preview, the structure is saved as JSON, and then integrated back into the email design.

Step 1: Clone and Setup

Clone the repository and install dependencies:

git clone https://github.com/BeefreeSDK/beefree-sdk-form-block-demo.git
cd beefree-sdk-form-block-demo
npm install

Create your .env file with Beefree SDK credentials:

BEE_CLIENT_ID=your-client-id-here
BEE_CLIENT_SECRET=your-client-secret-here
uid=demo-user

Step 2: The Form Builder Modal Structure

File to reference: form-builder-example.html (lines 146-169)

The form builder uses a modal interface with two main sections:

<!-- Custom Modal for Form Builder -->
<div id="form-modal" class="modal-overlay" style="display: none;">
  <div class="modal">
    <h2>Form Builder</h2>
    <div class="form-builder">
      <!-- Field Type Buttons -->
      <div class="field-list">
        <button onclick="addField('text')">Text</button>
        <button onclick="addField('number')">Number</button>
        <button onclick="addField('email')">Email</button>
        <button onclick="addField('date')">Date</button>
        <button onclick="addField('checkbox')">Checkbox</button>
        <button onclick="addField('select')">Select</button>
        <button onclick="addField('textarea')">Textarea</button>
        <button onclick="addField('password')">Password</button>
        <button onclick="addField('file')">File</button>
        <button onclick="addField('submit')">Submit</button>
      </div>
      <!-- Live Preview Area -->
      <div class="form-preview" id="form-preview">
        <!-- Form fields will be added here dynamically -->
      </div>
    </div>
    <button class="save-button" onclick="saveForm()">Save Form</button>
  </div>
</div>

Step 3: Form Structure Management

File to reference: form-builder-example.html (lines 213-229)

The form structure is managed as a JavaScript object that gets populated as users add fields:

var formStructure = {
  structure: {
    title: 'Custom Form',
    description: 'This is a custom form built by the user.',
    fields: {},           // Dynamic fields added by user
    layout: [],          // Dynamic layout based on field order
    attributes: {
      'accept-charset': 'UTF-8',
      action: 'http://example.com/read-form-script',
      autocomplete: 'on',
      enctype: 'multipart/form-data',
      method: 'post',
      novalidate: false,
      target: '_self',
    },
  },
};

Step 4: Content Dialog Integration

File to reference: form-builder-example.html (lines 271-282)

The key integration point is the contentDialog.manageForm handler:

beeConfig = {
  // ... other config options
  contentDialog: {
    manageForm: {
      label: 'Edit Form',
      handler: async (resolve, reject, args) => {
        // Open the form builder modal
        openModal();
        
        // Wait for user to build and save form
        await new Promise((res) => {
          window.saveForm = () => {
            resolve(formStructure);  // Return built form structure
            closeModal();            // Close the modal
          };
        });
      }
    }
  },
  defaultForm: formStructure,  // Use the dynamic form structure
  // ... other handlers
};

Step 5: Dynamic Field Addition

File to reference: form-builder-example.html (lines 383-417)

The addField() function handles dynamic field creation with live preview:

function addField(type) {
  const fieldId = `field-${Date.now()}`;  // Unique field ID
  const fieldLabel = prompt(`Enter label for ${type} field:`, `${type} Field`);

  if (fieldLabel) {
    // Create field definition
    const field = {
      type: type,
      label: fieldLabel,
      canBeRemovedFromLayout: true,
      removeFromLayout: false,
      attributes: {
        required: true,
      },
    };

    // Add options for select fields
    if (type === 'select') {
      field.options = [
        { type: 'option', label: 'Option 1', value: 'option1' },
        { type: 'option', label: 'Option 2', value: 'option2' },
      ];
    }

    // Add to form structure
    formStructure.structure.fields[fieldId] = field;
    formStructure.structure.layout.push([fieldId]);

    // Update live preview
    const formPreview = document.getElementById('form-preview');
    const fieldElement = document.createElement('div');
    fieldElement.className = 'form-field';
    fieldElement.innerHTML = `
      <label>${fieldLabel}</label>
      <input type="${type}" placeholder="${fieldLabel}" />
    `;
    formPreview.appendChild(fieldElement);
  }
}

Step 6: Field Type Configuration

The form builder supports multiple field types with automatic configuration:

// Text-based fields (text, email, password, etc.)
const textField = {
  type: 'text',
  label: 'Full Name',
  attributes: {
    required: true,
    placeholder: 'Enter your name',
    maxlength: 100
  }
};

// Number fields with validation
const numberField = {
  type: 'number',
  label: 'Age',
  attributes: {
    required: true,
    min: 18,
    max: 100,
    placeholder: 'Enter your age'
  }
};

// Select dropdowns with options
const selectField = {
  type: 'select',
  label: 'Country',
  attributes: { required: true },
  options: [
    { type: 'option', label: 'United States', value: 'us' },
    { type: 'option', label: 'Canada', value: 'ca' },
    { type: 'option', label: 'United Kingdom', value: 'uk' }
  ]
};

// File upload fields
const fileField = {
  type: 'file',
  label: 'Upload Document',
  attributes: {
    accept: '.pdf,.doc,.docx',
    required: false
  }
};

Step 7: Live Preview System

The form builder includes a real-time preview system that shows users exactly how their form will look:

function updatePreview(fieldId, field) {
  const formPreview = document.getElementById('form-preview');
  const fieldElement = document.createElement('div');
  fieldElement.className = 'form-field';
  fieldElement.id = `preview-${fieldId}`;
  
  let inputHTML = '';
  switch (field.type) {
    case 'select':
      inputHTML = `
        <select>
          ${field.options.map(opt => 
            `<option value="${opt.value}">${opt.label}</option>`
          ).join('')}
        </select>
      `;
      break;
    case 'textarea':
      inputHTML = `<textarea placeholder="${field.label}"></textarea>`;
      break;
    case 'checkbox':
      inputHTML = `<input type="checkbox" /> ${field.label}`;
      break;
    default:
      inputHTML = `<input type="${field.type}" placeholder="${field.label}" />`;
  }
  
  fieldElement.innerHTML = `
    <label>${field.label}</label>
    ${inputHTML}
  `;
  
  formPreview.appendChild(fieldElement);
}

Step 8: Modal Management

File to reference: form-builder-example.html (lines 374-381)

Simple modal controls for opening and closing the form builder:

// Modal Functions
function openModal() {
  document.getElementById('form-modal').style.display = 'flex';
  // Clear previous form data
  document.getElementById('form-preview').innerHTML = '';
  formStructure.structure.fields = {};
  formStructure.structure.layout = [];
}

function closeModal() {
  document.getElementById('form-modal').style.display = 'none';
}

Step 9: Form Validation and Schema

Before saving, you can add validation to ensure the form structure is valid:

function validateFormStructure(structure) {
  // Check required properties
  if (!structure.title || !structure.fields) {
    throw new Error('Form must have a title and fields');
  }
  
  // Validate field types
  const validTypes = ['text', 'email', 'number', 'select', 'checkbox', 'textarea', 'password', 'file', 'submit'];
  for (const [fieldId, field] of Object.entries(structure.fields)) {
    if (!validTypes.includes(field.type)) {
      throw new Error(`Invalid field type: ${field.type}`);
    }
    
    // Validate select fields have options
    if (field.type === 'select' && (!field.options || field.options.length === 0)) {
      throw new Error(`Select field ${fieldId} must have options`);
    }
  }
  
  return true;
}

// Enhanced save function
window.saveForm = () => {
  try {
    validateFormStructure(formStructure.structure);
    resolve(formStructure);
    closeModal();
  } catch (error) {
    alert('Form validation error: ' + error.message);
  }
};

Step 10: Advanced Field Configuration

For more sophisticated forms, you can enhance the field addition process:

function addAdvancedField(type) {
  const fieldId = `field-${Date.now()}`;
  
  // Show configuration modal for field
  const config = showFieldConfigModal(type);
  
  if (config) {
    const field = {
      type: type,
      label: config.label,
      canBeRemovedFromLayout: config.removable !== false,
      removeFromLayout: false,
      canBeModified: config.modifiable !== false,
      attributes: {
        required: config.required || false,
        placeholder: config.placeholder || '',
        ...config.customAttributes
      }
    };
    
    // Add field-specific configurations
    switch (type) {
      case 'select':
      case 'radio':
        field.options = config.options || [];
        break;
      case 'number':
        if (config.min) field.attributes.min = config.min;
        if (config.max) field.attributes.max = config.max;
        break;
      case 'text':
        if (config.maxlength) field.attributes.maxlength = config.maxlength;
        if (config.pattern) field.attributes.pattern = config.pattern;
        break;
    }
    
    formStructure.structure.fields[fieldId] = field;
    formStructure.structure.layout.push([fieldId]);
    updatePreview(fieldId, field);
  }
}

Step 11: Running the Form Builder

Start the proxy server:

node proxy-server.js

Open form-builder-example.html in your browser:

  1. SDK loads with authentication via the proxy

  2. Drag a form block onto the design stage

  3. Click "Edit Form" to open the form builder modal

  4. Add fields using the field type buttons

  5. Preview in real-time as you build your form

  6. Save the form to integrate it into your page or popup design

Step 12: CSS Styling

File to reference: form-builder-example.html (lines 38-132)

The form builder includes responsive CSS for a custom interface:

.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.form-builder {
  display: flex;
  gap: 20px;
}

.field-list {
  width: 200px;
  border: 1px solid #ddd;
  padding: 10px;
  border-radius: 4px;
}

.form-preview {
  flex: 1;
  border: 1px solid #ddd;
  padding: 10px;
  border-radius: 4px;
  min-height: 300px;
}

Advanced Features

Customize the form builder further with your additions and styling. A few customization examples include:

  • Form Templates: Save commonly used form structures as templates:

  • Field Reordering: Add drag-and-drop reordering for form fields:

Learn More

Implementation Checklist

  • Set up modal interface with field buttons and preview area

  • Configure contentDialog.manageForm handler for SDK integration

  • Implement dynamic field addition with live preview updates

  • Add form validation for structure integrity

  • Style the interface for professional appearance

  • Test the complete workflow from form building to design integration

  • Customize the form builder by adding your own advanced features like templates and field reordering

Clone the example project, explore the form-builder-example.html file, and customize the form builder interface to match your application's needs. Your users will then have a powerful tool for creating custom forms directly within their page and popup designs.

Last updated

Was this helpful?