Skip to content
Journeybee Help Center home
Journeybee Help Center home

Embeddable Forms

Embeddable Forms

Overview

JourneyBee Forms provides a secure, customisable, and easy-to-embed form solution for collecting leads and data from your website or application. This guide covers everything you need to know about embedding JourneyBee forms, from basic implementation to advanced security and customisation features.


Quick Start

Basic Embedding

Embed a form in just 3 simple steps:

<!-- 1. Add the JourneyBee SDK script --> <script src="https://forms.journeybee.io/api/embed/sdk.js" async></script> <!-- 2. Create a container for your form --> <div id="my-form-container"></div> <!-- 3. Initialize the form --> <script> // Wait for the script to load window.addEventListener('load', function() { window.journeybee('init', 'your-form-uuid', document.getElementById('my-form-container'), { companyName: 'your-company-name', // Optional: defaults to 'journeybee' debug: false // Optional: enable for development }); }); </script>

Framework Integration

React/Next.js Integration

For React applications, use this component pattern:

import { useEffect, useRef } from 'react'; function JourneyBeeForm({ formUuid, companyName, options = {} }) { const containerRef = useRef(null); const formRef = useRef(null); useEffect(() => { // Load the SDK script const script = document.createElement('script'); script.src = 'https://forms.journeybee.io/api/embed/sdk.js'; script.async = true; script.onload = () => { if (containerRef.current && window.journeybee) { formRef.current = window.journeybee('init', formUuid, containerRef.current, { companyName, debug: process.env.NODE_ENV === 'development', ...options }); } }; document.head.appendChild(script); // Cleanup return () => { if (formRef.current) { window.journeybee('destroy', formUuid); } document.head.removeChild(script); }; }, [formUuid, companyName, options]); return <div ref={containerRef} style={{ width: '100%', minHeight: '400px' }} />; } export default JourneyBeeForm;

Vue.js Integration

<template> <div ref="formContainer" class="journeybee-form"></div> </template> <script> export default { name: 'JourneyBeeForm', props: { formUuid: { type: String, required: true }, companyName: { type: String, default: 'journeybee' } }, mounted() { this.loadForm(); }, beforeUnmount() { if (this.form) { window.journeybee('destroy', this.formUuid); } }, methods: { loadForm() { const script = document.createElement('script'); script.src = 'https://forms.journeybee.io/api/embed/sdk.js'; script.async = true; script.onload = () => { this.form = window.journeybee('init', this.formUuid, this.$refs.formContainer, { companyName: this.companyName, debug: process.env.NODE_ENV === 'development' }); }; document.head.appendChild(script); } } }; </script> <style scoped> .journeybee-form { width: 100%; min-height: 400px; } </style>

Advanced Configuration

Form Options

The journeybee('init') method accepts various options for customisation:

window.journeybee('init', formUuid, container, { // Basic Options companyName: 'your-company-name', debug: false, // Theming customization: { theme: { colors: { primary: '#3b82f6', secondary: '#f1f5f9', background: '#ffffff', text: '#1e293b', border: '#cbd5e1', error: '#ef4444', success: '#22c55e' }, typography: { fontFamily: 'Inter, sans-serif', fontSize: { base: '16px', label: '14px', input: '16px' }, fontWeight: 'normal' // 'normal' | 'medium' | 'semibold' | 'bold' }, spacing: { padding: '24px', gap: '16px' }, borders: { radius: '8px', width: '1px', style: 'solid' } } }, // Pre-fill form data prefill: { first_name: 'John', last_name: 'Doe', email: 'john@example.com', phone_number: '+1 555 123 4567', company_name: 'Acme Corp' }, // Event Callbacks onReady: function() { console.log('Form is ready'); }, onSuccess: function(data) { console.log('Form submitted successfully:', data); // Hide modal, show success message, redirect, etc. }, onError: function(error) { console.error('Form submission failed:', error); // Show error message, retry logic, etc. }, onValidation: function(validation) { console.log('Form validation:', validation); } });

Theme Presets

Use predefined themes for quick styling:

// Dark theme customization: { theme: { colors: { primary: '#ffffff', secondary: '#1f2937', background: '#111827', text: '#ffffff', border: '#374151', error: '#ef4444' } } } // Blue theme customization: { theme: { colors: { primary: '#3b82f6', secondary: '#eff6ff', background: '#ffffff', text: '#1e293b', border: '#cbd5e1' } } } // Minimal theme customization: { theme: { colors: { primary: '#000000', background: '#ffffff', text: '#000000', border: '#e5e5e5' }, borders: { radius: '0px' // Square corners } } }

Field Customisation

Customize individual form fields:

customization: { fields: { first_name: { label: { text: 'Your First Name', required: true }, placeholder: 'Enter your first name', defaultValue: 'John' }, email: { validation: { required: true, pattern: '^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$', customMessage: 'Please enter a valid email address' } }, company_name: { label: { hide: true // Hide the label }, placeholder: 'Company Name (Optional)' } }, layout: { title: 'Get in Touch', description: 'Fill out this form and we\'ll get back to you within 24 hours.', submitButton: { text: 'Send Message', position: 'center' }, showBranding: false // Hide JourneyBee branding } }

Form Instance Methods

The journeybee('init') method returns a form instance with additional methods for advanced control:

const form = window.journeybee('init', formUuid, container, options); // Update theme/styling dynamically form.updateCustomization({ theme: { colors: { primary: '#10b981' } } }); // Pre-fill form data programmatically form.setFormData({ first_name: 'Jane', last_name: 'Smith', email: 'jane@example.com' }); // Apply custom CSS styles form.setStyle({ '--form-background': '#f9fafb', '--input-border-color': '#d1d5db' }); // Send custom messages to the form form.sendMessage({ type: 'customizationUpdate', customization: { /* ... */ } }); // Destroy the form instance form.destroy(); // Or use: window.journeybee('destroy', formUuid);

Modal/Popup Implementation

Basic Implementation

<!DOCTYPE html> <html> <head> <title>JourneyBee Form Modal</title> <style> .modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); } .modal-content { position: relative; background-color: white; margin: 5% auto; padding: 0; width: 90%; max-width: 600px; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); max-height: 90vh; overflow-y: auto; } .close { position: absolute; right: 15px; top: 15px; font-size: 28px; font-weight: bold; cursor: pointer; z-index: 1001; } #form-container { width: 100%; min-height: 400px; } </style> </head> <body> <!-- Trigger button --> <button id="open-form">Open Contact Form</button> <!-- Modal --> <div id="form-modal" class="modal"> <div class="modal-content"> <span class="close">&times;</span> <div id="form-container"></div> </div> </div> <script src="https://forms.journeybee.io/api/embed/sdk.js" async></script> <script> let form = null; const modal = document.getElementById('form-modal'); const openBtn = document.getElementById('open-form'); const closeBtn = document.querySelector('.close'); openBtn.onclick = function() { modal.style.display = 'block'; // Initialize form when modal opens (only once) if (!form) { form = window.journeybee('init', 'your-form-uuid', document.getElementById('form-container'), { companyName: 'your-company', onSuccess: function(data) { console.log('Form submitted:', data); modal.style.display = 'none'; alert('Thank you! We\'ll be in touch soon.'); }, onError: function(error) { console.error('Form error:', error); alert('Something went wrong. Please try again.'); } }); } }; closeBtn.onclick = function() { modal.style.display = 'none'; }; window.onclick = function(event) { if (event.target === modal) { modal.style.display = 'none'; } }; </script> </body> </html>

React Modal with Form

import React, { useState, useEffect, useRef } from 'react'; function ContactModal({ isOpen, onClose, formUuid, companyName }) { const containerRef = useRef(null); const formRef = useRef(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { if (isOpen && !formRef.current) { // Load SDK script const script = document.createElement('script'); script.src = 'https://forms.journeybee.io/api/embed/sdk.js'; script.async = true; script.onload = () => { if (containerRef.current && window.journeybee) { formRef.current = window.journeybee('init', formUuid, containerRef.current, { companyName, onReady: () => setIsLoading(false), onSuccess: (data) => { console.log('Form submitted:', data); onClose(); // Show success notification }, onError: (error) => { console.error('Form error:', error); // Show error notification } }); } }; document.head.appendChild(script); } return () => { if (formRef.current && !isOpen) { window.journeybee('destroy', formUuid); formRef.current = null; } }; }, [isOpen, formUuid, companyName, onClose]); if (!isOpen) return null; return ( <div className="modal-overlay" onClick={onClose}> <div className="modal-content" onClick={e => e.stopPropagation()}> <button className="modal-close" onClick={onClose}>×</button> {isLoading && <div className="loading">Loading form...</div>} <div ref={containerRef} style={{ width: '100%', minHeight: '400px', display: isLoading ? 'none' : 'block' }} /> </div> </div> ); } export default ContactModal;

Security Considerations

Content Security Policy (CSP)

JourneyBee forms are designed to work with strict CSP policies. Add these directives to your CSP header:

Content-Security-Policy: script-src 'self' https://cdnjs.cloudflare.com https://forms.journeybee.io; frame-src 'self' https://forms.journeybee.io; connect-src 'self' https://forms.journeybee.io; style-src 'self' 'unsafe-inline';

Origin Validation

JourneyBee automatically validates the origin of embedded forms. To configure allowed origins:

  • Production: Set the ALLOWED_ORIGINS environment variable on your JourneyBee forms instance:

    ALLOWED_ORIGINS=https://yoursite.com,https://www.yoursite.com,https://app.yoursite.com
  • Development: Local origins (localhost, 127.0.0.1) are automatically allowed in development mode.

Data Privacy

  • No Sensitive Data Storage: JourneyBee forms don't store sensitive data like passwords or payment information

  • GDPR Compliant: All data collection respects GDPR requirements

  • Data Encryption: All data transmission uses HTTPS/TLS encryption

  • Cross-Origin Security: Strict origin validation prevents unauthorised embedding

Best Practices

  • Always use HTTPS in production

  • Validate origins by setting ALLOWED_ORIGINS

  • Implement CSP headers on your site

  • Monitor form submissions for unusual activity

  • Regular security updates - keep the SDK up to date


Error Handling

Common Issues and Solutions

1. Form Not Loading

// Problem: Form container not found // Solution: Ensure container exists before initialization window.addEventListener('load', function() { const container = document.getElementById('form-container'); if (!container) { console.error('Form container not found'); return; } window.journeybee('init', formUuid, container, options); });

2. CORS/Origin Issues

// Problem: Origin not allowed // Solution: Check ALLOWED_ORIGINS configuration // Development: Should work automatically // Production: Add your domain to ALLOWED_ORIGINS

3. Styling Conflicts

/* Problem: Parent styles affecting form */ /* Solution: Create isolated container */ .form-container { all: initial; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.5; }

4. Form not resizing

/* Problem: Form height not adjusting */ /* Solution: Ensure parent container allows height changes */ .form-container { height: auto !important; min-height: 400px; overflow: visible; }

Error Event Handling

window.journeybee('init', formUuid, container, { onError: function(error) { switch(error.code) { case 'NETWORK_ERROR': showNotification('Network error. Please check your connection.', 'error'); break; case 'VALIDATION_ERROR': showNotification('Please check your form inputs.', 'warning'); break; case 'DUPLICATE_LEAD': showNotification('This information has already been submitted.', 'info'); break; case 'INVALID_PARTNERSHIP': showNotification('Invalid form configuration. Please contact support.', 'error'); break; default: showNotification('Something went wrong. Please try again.', 'error'); } } });

Performance Optimization

Lazy Loading

Load forms only when needed to improve page performance:

// Intersection Observer for lazy loading const formObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { loadJourneyBeeForm(entry.target); formObserver.unobserve(entry.target); } }); }); // Observe form containers document.querySelectorAll('.lazy-form').forEach(container => { formObserver.observe(container); }); function loadJourneyBeeForm(container) { const script = document.createElement('script'); script.src = 'https://forms.journeybee.io/api/embed/sdk.js'; script.async = true; script.onload = () => { window.journeybee('init', container.dataset.formUuid, container, { companyName: container.dataset.companyName }); }; document.head.appendChild(script); }

Preloading

For critical forms, preload the SDK:

<link rel="preload" href="https://forms.journeybee.io/api/embed/sdk.js" as="script">

Analytics and Tracking

Google Analytics Integration

window.journeybee('init', formUuid, container, { onReady: function() { // Track form view gtag('event', 'form_view', { event_category: 'engagement', event_label: 'contact_form' }); }, onSuccess: function(data) { // Track successful submission gtag('event', 'form_submit', { event_category: 'conversion', event_label: 'contact_form', value: 1 }); }, onError: function(error) { // Track errors gtag('event', 'form_error', { event_category: 'error', event_label: error.code || 'unknown' }); } });

Custom Event Tracking

// Listen for form events window.addEventListener('message', function(event) { if (event.origin !== 'https://forms.journeybee.io') return; const { type, formUuid } = event.data; switch(type) { case 'formSubmission': // Custom tracking logic trackFormSubmission(formUuid, event.data); break; case 'error': // Error tracking trackFormError(formUuid, event.data); break; } });

API Reference

Global Methods

journeybee('init', formUuid, container, options)

Initialise a new form instance.

Parameters:

  • formUuid (string): The unique identifier for your form

  • container (HTMLElement): The DOM element to render the form in

  • options (object): Configuration options

Returns: Form instance object

journeybee('destroy', formUuid)

Destroy a form instance and clean up resources.

Parameters:

  • formUuid (string): The form UUID to destroy

Instance Methods

When you call journeybee('init'), it returns a form instance with these methods:

Method

Description

updateCustomization(customization)

Update theme and styling dynamically

setFormData(data)

Pre-fill form fields programmatically

setStyle(styles)

Apply custom CSS variables

sendMessage(message)

Send custom messages to the form iframe

destroy()

Remove the form and clean up resources

Events

Forms emit various events via postMessage that you can listen to:

// Listen to all form messages window.addEventListener('message', function(event) { if (event.origin !== 'https://forms.journeybee.io') return; console.log('Form event:', event.data); });

Event Types:

Event

Description

ready

Form is loaded and ready

formSubmission

Form was submitted (check status for success/error)

formValidation

Form validation state changed

error

An error occurred

Configuration Schema

The complete configuration schema:

interface FormOptions { companyName?: string; debug?: boolean; customization?: { theme?: { colors?: { primary?: string; // Hex, rgb, or rgba secondary?: string; background?: string; text?: string; border?: string; error?: string; success?: string; }; typography?: { fontFamily?: string; fontSize?: { base?: string; // e.g., '16px', '1rem' label?: string; input?: string; }; fontWeight?: 'normal' | 'medium' | 'semibold' | 'bold'; }; spacing?: { padding?: string; // e.g., '24px', '1.5rem' gap?: string; }; borders?: { width?: string; radius?: 'none' | 'sm' | 'md' | 'lg' | 'full' | string; style?: 'solid' | 'dashed' | 'dotted'; }; }; fields?: { [fieldName: string]: { label?: { text?: string; required?: boolean; hide?: boolean; }; placeholder?: string; defaultValue?: string | number | boolean; validation?: { required?: boolean; pattern?: string; minLength?: number; maxLength?: number; min?: number; max?: number; customMessage?: string; }; }; }; layout?: { title?: string; description?: string; submitButton?: { text?: string; position?: 'left' | 'center' | 'right'; }; showBranding?: boolean; }; }; prefill?: { first_name?: string; last_name?: string; email?: string; phone_number?: string; company_name?: string; [key: string]: any; // Custom fields }; onReady?: () => void; onSuccess?: (data: any) => void; onError?: (error: any) => void; onValidation?: (validation: any) => void; }

Troubleshooting

Common Issues

Issue

Solution

Form not appearing

Check console for errors, verify container exists

Styling issues

Check for CSS conflicts, use specific selectors

Submission errors

Verify form UUID and company name are correct

CORS errors

Check ALLOWED_ORIGINS configuration

Form not resizing

Ensure parent container has height: auto and overflow: visible


Support

For additional support email us at engineering@journeybee.io