Widget Customization

Widget Customization Guide

Complete guide to customizing the Waitlist Widget embed widget. 🎨

Table of Contents


Configuration Options

Basic widget initialization:

WaitlistWidget.init({
  apiKey: 'YOUR_API_KEY',           // Required
  containerId: 'waitlist-widget',   // Required
  
  // Optional configuration
  placeholder: 'Enter your email...',
  buttonText: 'Join Waitlist',
  successMessage: 'Thanks for joining!',
  errorMessage: 'Something went wrong. Please try again.',
  
  // Styling options
  theme: 'light',                   // 'light' | 'dark' | 'auto'
  primaryColor: '#3b82f6',
  borderRadius: '8px',
  
  // Callbacks
  onSuccess: (data) => {},
  onError: (error) => {},
  onSubmit: () => {}
});

Configuration Reference

OptionTypeDefaultDescription
apiKeystringRequiredYour project's API key
containerIdstringRequiredID of the container element
placeholderstring"Enter your email..."Input placeholder text
buttonTextstring"Join Waitlist"Submit button text
successMessagestring"Thanks for joining our waitlist!"Success notification text
errorMessagestring"Something went wrong. Please try again."Generic error message
theme'light' | 'dark' | 'auto''light'Color theme
primaryColorstring'#3b82f6'Primary accent color (hex/rgb)
borderRadiusstring'8px'Border radius for inputs/buttons
onSuccessfunctionnullCallback on successful submission
onErrorfunctionnullCallback on error
onSubmitfunctionnullCallback before submission

CSS Class Structure

The widget uses these CSS classes for styling:

<div id="waitlist-widget" class="waitlist-container">
  <form class="waitlist-form">
    <div class="waitlist-input-wrapper">
      <input 
        type="email" 
        class="waitlist-input" 
        placeholder="Enter your email..."
      />
      <span class="waitlist-input-icon">📧</span>
    </div>
    
    <button type="submit" class="waitlist-button">
      <span class="waitlist-button-text">Join Waitlist</span>
      <span class="waitlist-button-loading" style="display: none;">⏳</span>
    </button>
    
    <div class="waitlist-message waitlist-message-success" style="display: none;">
      Thanks for joining our waitlist!
    </div>
    
    <div class="waitlist-message waitlist-message-error" style="display: none;">
      Something went wrong. Please try again.
    </div>
  </form>
</div>

Class Reference

ClassDescription
.waitlist-containerMain wrapper div
.waitlist-formForm element
.waitlist-input-wrapperInput wrapper (for icons, etc.)
.waitlist-inputEmail input field
.waitlist-input-iconInput icon (optional)
.waitlist-buttonSubmit button
.waitlist-button-textButton text span
.waitlist-button-loadingLoading indicator (shown during submit)
.waitlist-messageMessage container (success/error)
.waitlist-message-successSuccess message styling
.waitlist-message-errorError message styling

State Classes

ClassApplied When
.waitlist-form--loadingForm is submitting
.waitlist-form--successSuccessful submission
.waitlist-form--errorError occurred
.waitlist-input--errorInput validation failed
.waitlist-button--disabledButton is disabled

Custom Styling Examples

Example 1: Gradient Theme

<style>
  /* Gradient background theme */
  .waitlist-form {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    padding: 2rem;
    border-radius: 16px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
  }
  
  .waitlist-input {
    width: 100%;
    padding: 14px 16px;
    font-size: 16px;
    border: 2px solid rgba(255, 255, 255, 0.3);
    border-radius: 12px;
    background: rgba(255, 255, 255, 0.9);
    color: #1a202c;
    transition: all 0.3s ease;
  }
  
  .waitlist-input:focus {
    outline: none;
    border-color: rgba(255, 255, 255, 0.8);
    background: #ffffff;
    box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.2);
  }
  
  .waitlist-button {
    width: 100%;
    margin-top: 12px;
    padding: 14px 24px;
    font-size: 16px;
    font-weight: 600;
    color: #764ba2;
    background: #ffffff;
    border: none;
    border-radius: 12px;
    cursor: pointer;
    transition: all 0.3s ease;
  }
  
  .waitlist-button:hover {
    background: #f7fafc;
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  }
  
  .waitlist-message-success {
    margin-top: 12px;
    padding: 12px;
    color: #ffffff;
    background: rgba(72, 187, 120, 0.3);
    border-radius: 8px;
    text-align: center;
  }
</style>

Example 2: Minimal Dark Theme

<style>
  /* Minimal dark theme */
  .waitlist-form {
    background: #1a1a1a;
    padding: 1.5rem;
    border-radius: 8px;
    border: 1px solid #333;
  }
  
  .waitlist-input {
    width: 100%;
    padding: 12px 16px;
    font-size: 15px;
    color: #ffffff;
    background: #2a2a2a;
    border: 1px solid #444;
    border-radius: 6px;
    transition: border-color 0.2s ease;
  }
  
  .waitlist-input:focus {
    outline: none;
    border-color: #4ade80;
  }
  
  .waitlist-input::placeholder {
    color: #888;
  }
  
  .waitlist-button {
    width: 100%;
    margin-top: 10px;
    padding: 12px;
    font-size: 15px;
    font-weight: 500;
    color: #1a1a1a;
    background: #4ade80;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.2s ease;
  }
  
  .waitlist-button:hover {
    background: #22c55e;
  }
  
  .waitlist-button:disabled {
    background: #444;
    color: #888;
    cursor: not-allowed;
  }
  
  .waitlist-message {
    margin-top: 10px;
    padding: 10px;
    font-size: 14px;
    border-radius: 6px;
    text-align: center;
  }
  
  .waitlist-message-success {
    color: #4ade80;
    background: rgba(74, 222, 128, 0.1);
    border: 1px solid #4ade80;
  }
  
  .waitlist-message-error {
    color: #f87171;
    background: rgba(248, 113, 113, 0.1);
    border: 1px solid #f87171;
  }
</style>

Example 3: Modern Glassmorphism

<style>
  /* Glassmorphism theme */
  body {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  }
  
  .waitlist-form {
    background: rgba(255, 255, 255, 0.1);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    padding: 2rem;
    border-radius: 20px;
    border: 1px solid rgba(255, 255, 255, 0.2);
    box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
  }
  
  .waitlist-input {
    width: 100%;
    padding: 14px 16px;
    font-size: 16px;
    color: #ffffff;
    background: rgba(255, 255, 255, 0.1);
    backdrop-filter: blur(5px);
    border: 1px solid rgba(255, 255, 255, 0.3);
    border-radius: 12px;
    transition: all 0.3s ease;
  }
  
  .waitlist-input::placeholder {
    color: rgba(255, 255, 255, 0.6);
  }
  
  .waitlist-input:focus {
    outline: none;
    background: rgba(255, 255, 255, 0.15);
    border-color: rgba(255, 255, 255, 0.5);
  }
  
  .waitlist-button {
    width: 100%;
    margin-top: 12px;
    padding: 14px;
    font-size: 16px;
    font-weight: 600;
    color: #667eea;
    background: rgba(255, 255, 255, 0.9);
    border: none;
    border-radius: 12px;
    cursor: pointer;
    transition: all 0.3s ease;
  }
  
  .waitlist-button:hover {
    background: #ffffff;
    transform: translateY(-2px);
  }
  
  .waitlist-message-success {
    margin-top: 12px;
    padding: 12px;
    color: #ffffff;
    background: rgba(72, 187, 120, 0.2);
    backdrop-filter: blur(5px);
    border: 1px solid rgba(255, 255, 255, 0.3);
    border-radius: 12px;
    text-align: center;
  }
</style>

Example 4: Inline/Horizontal Layout

<style>
  /* Horizontal inline form */
  .waitlist-form {
    display: flex;
    gap: 8px;
    align-items: center;
    padding: 0;
  }
  
  .waitlist-input {
    flex: 1;
    padding: 12px 16px;
    font-size: 15px;
    border: 2px solid #e5e7eb;
    border-radius: 8px;
    transition: border-color 0.2s ease;
  }
  
  .waitlist-input:focus {
    outline: none;
    border-color: #3b82f6;
  }
  
  .waitlist-button {
    flex-shrink: 0;
    padding: 12px 24px;
    font-size: 15px;
    font-weight: 600;
    color: #ffffff;
    background: #3b82f6;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.2s ease;
  }
  
  .waitlist-button:hover {
    background: #2563eb;
  }
  
  .waitlist-message {
    width: 100%;
    margin-top: 8px;
    padding: 10px;
    font-size: 14px;
    border-radius: 6px;
    text-align: center;
  }
  
  .waitlist-message-success {
    color: #059669;
    background: #d1fae5;
  }
  
  .waitlist-message-error {
    color: #dc2626;
    background: #fee2e2;
  }
  
  /* Mobile responsive */
  @media (max-width: 640px) {
    .waitlist-form {
      flex-direction: column;
    }
    
    .waitlist-button {
      width: 100%;
    }
  }
</style>

JavaScript Callbacks

onSuccess Callback

Called when a user successfully subscribes.

WaitlistWidget.init({
  apiKey: 'YOUR_API_KEY',
  containerId: 'waitlist-widget',
  onSuccess: (data) => {
    console.log('User subscribed!', data);
    
    // Example: Track with analytics
    if (window.gtag) {
      gtag('event', 'waitlist_signup', {
        email: data.subscriber.email
      });
    }
    
    // Example: Show custom modal
    showThankYouModal();
    
    // Example: Redirect to thank you page
    setTimeout(() => {
      window.location.href = '/thank-you';
    }, 2000);
  }
});

Callback Data:

{
  success: true,
  subscriber: {
    id: string,
    email: string,
    subscribedAt: string
  },
  message: string
}

onError Callback

Called when an error occurs (validation, network, duplicate email, etc.).

WaitlistWidget.init({
  apiKey: 'YOUR_API_KEY',
  containerId: 'waitlist-widget',
  onError: (error) => {
    console.error('Subscription failed:', error);
    
    // Handle specific error codes
    switch (error.code) {
      case 'EMAIL_ALREADY_SUBSCRIBED':
        alert('You\'re already on the waitlist!');
        break;
      
      case 'VALIDATION_ERROR':
        alert('Please enter a valid email address.');
        break;
      
      case 'RATE_LIMIT_EXCEEDED':
        alert('Too many attempts. Please try again in a minute.');
        break;
      
      default:
        alert('Something went wrong. Please try again later.');
    }
    
    // Example: Track error with analytics
    if (window.gtag) {
      gtag('event', 'waitlist_error', {
        error_code: error.code,
        error_message: error.message
      });
    }
  }
});

Error Data:

{
  success: false,
  error: {
    code: string,
    message: string,
    details?: any
  }
}

Common Error Codes:

CodeDescriptionAction
EMAIL_ALREADY_SUBSCRIBEDEmail already existsShow "Already subscribed" message
VALIDATION_ERRORInvalid email formatShow validation error
RATE_LIMIT_EXCEEDEDToo many requestsAsk user to wait
INVALID_API_KEYAPI key is invalidCheck configuration
NETWORK_ERRORConnection failedCheck internet connection

onSubmit Callback

Called before the form is submitted. Return false to cancel submission.

WaitlistWidget.init({
  apiKey: 'YOUR_API_KEY',
  containerId: 'waitlist-widget',
  onSubmit: (email) => {
    console.log('About to submit:', email);
    
    // Example: Custom validation
    if (email.endsWith('@competitor.com')) {
      alert('Sorry, competitor emails are not allowed.');
      return false; // Cancel submission
    }
    
    // Example: Confirm before submitting
    const confirmed = confirm(`Join waitlist with ${email}?`);
    return confirmed; // true = continue, false = cancel
  }
});

Complete Callback Example

WaitlistWidget.init({
  apiKey: 'wls_live_abc123...',
  containerId: 'waitlist-widget',
  
  // Styling
  theme: 'dark',
  primaryColor: '#10b981',
  buttonText: 'Get Early Access',
  
  // Callbacks
  onSubmit: (email) => {
    console.log('Submitting:', email);
    
    // Show loading indicator
    document.getElementById('custom-loader').style.display = 'block';
    
    return true; // Continue submission
  },
  
  onSuccess: (data) => {
    console.log('Success!', data);
    
    // Hide loading indicator
    document.getElementById('custom-loader').style.display = 'none';
    
    // Track conversion
    if (window.gtag) {
      gtag('event', 'conversion', {
        send_to: 'AW-CONVERSION_ID/CONVERSION_LABEL'
      });
    }
    
    // Store in localStorage
    localStorage.setItem('waitlist_joined', 'true');
    localStorage.setItem('waitlist_email', data.subscriber.email);
    
    // Redirect after 3 seconds
    setTimeout(() => {
      window.location.href = '/thank-you?email=' + encodeURIComponent(data.subscriber.email);
    }, 3000);
  },
  
  onError: (error) => {
    console.error('Error:', error);
    
    // Hide loading indicator
    document.getElementById('custom-loader').style.display = 'none';
    
    // Custom error handling
    let message = 'Something went wrong. Please try again.';
    
    if (error.code === 'EMAIL_ALREADY_SUBSCRIBED') {
      message = 'Great! You\'re already on our waitlist. Check your email for updates.';
      // Treat as success
      localStorage.setItem('waitlist_joined', 'true');
    } else if (error.code === 'RATE_LIMIT_EXCEEDED') {
      message = 'Whoa, slow down! Please wait a minute before trying again.';
    }
    
    // Show custom notification
    showNotification(message, error.code === 'EMAIL_ALREADY_SUBSCRIBED' ? 'success' : 'error');
    
    // Track error
    if (window.gtag) {
      gtag('event', 'exception', {
        description: error.code,
        fatal: false
      });
    }
  }
});

Accessibility

The widget is designed with accessibility in mind:

Built-in Features

  • Semantic HTML: Proper form elements
  • Keyboard navigation: Tab, Enter to submit
  • ARIA labels: Screen reader support
  • Focus management: Clear focus indicators
  • Error announcements: Screen reader feedback
  • High contrast: WCAG AA compliant colors

Enhancing Accessibility

<!-- Add descriptive label -->
<label for="waitlist-widget" class="sr-only">
  Join our waitlist to get early access
</label>
<div id="waitlist-widget"></div>

<style>
  /* Screen reader only class */
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
  }
  
  /* Focus indicators */
  .waitlist-input:focus,
  .waitlist-button:focus {
    outline: 2px solid #3b82f6;
    outline-offset: 2px;
  }
  
  /* High contrast mode */
  @media (prefers-contrast: high) {
    .waitlist-input {
      border-width: 2px;
    }
  }
  
  /* Reduced motion */
  @media (prefers-reduced-motion: reduce) {
    .waitlist-button,
    .waitlist-input {
      transition: none;
    }
  }
</style>

Advanced Customization

Custom HTML Structure

If you need complete control, you can build your own form and use the API directly:

<form id="custom-waitlist-form">
  <input type="email" id="custom-email" placeholder="Your email" required />
  <button type="submit">Join Waitlist</button>
  <div id="custom-message"></div>
</form>

<script>
  document.getElementById('custom-waitlist-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    
    const email = document.getElementById('custom-email').value;
    const messageDiv = document.getElementById('custom-message');
    
    try {
      const response = await fetch('https://api.waitlistwidget.com/api/public/subscribe', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          email: email,
          apiKey: 'YOUR_API_KEY'
        })
      });
      
      const data = await response.json();
      
      if (data.success) {
        messageDiv.textContent = 'Thanks for joining!';
        messageDiv.style.color = 'green';
      } else {
        messageDiv.textContent = data.error.message;
        messageDiv.style.color = 'red';
      }
    } catch (error) {
      messageDiv.textContent = 'Network error. Please try again.';
      messageDiv.style.color = 'red';
    }
  });
</script>

Programmatic Control

// Initialize widget
const widget = WaitlistWidget.init({
  apiKey: 'YOUR_API_KEY',
  containerId: 'waitlist-widget'
});

// Submit programmatically
widget.submit('user@example.com');

// Reset form
widget.reset();

// Destroy widget
widget.destroy();

// Update configuration
widget.updateConfig({
  buttonText: 'Get Early Access',
  primaryColor: '#10b981'
});

Troubleshooting

Widget not rendering?

  1. Check container ID matches
  2. Ensure script is loaded (window.WaitlistWidget exists)
  3. Check browser console for errors
  4. Verify API key is correct

Styles not applying?

  1. Check CSS specificity (use !important if needed)
  2. Ensure styles load after widget script
  3. Inspect element to verify class names
  4. Check for conflicting global styles

Mobile responsiveness issues?

/* Make widget responsive */
.waitlist-form {
  max-width: 100%;
  width: 100%;
}

.waitlist-input,
.waitlist-button {
  width: 100%;
  box-sizing: border-box;
}

@media (max-width: 640px) {
  .waitlist-form {
    padding: 1rem;
  }
  
  .waitlist-input,
  .waitlist-button {
    font-size: 16px; /* Prevent zoom on iOS */
  }
}

Theme Examples

Want complete theme examples? Contact us at support@waitlistwidget.com for ready-to-use templates:

  • Gradient Theme - Modern gradient backgrounds
  • Dark Theme - Perfect for dark mode sites
  • Glassmorphism - Trendy frosted glass effect
  • Inline Layout - Compact single-line design

All themes include full HTML/CSS code and are production-ready!


Next Steps


Need help? Email us at support@waitlistwidget.com - we typically respond within 24 hours!