Features Pricing
Start My Free Trial

Embed Widget

Add a waitlist signup form to any website with one line of code.

MakeEmWait provides two embed variants: embed.js (styled, with Shadow DOM) and embed-plain.js (unstyled, no Shadow DOM). Both render a fully functional signup form wherever the script tag is placed.

Quick Start

Add this script tag anywhere on your page:

<script src="https://makeemwait.com/assets/js/embed.js"
        data-waitlist-id="YOUR_WAITLIST_ID"></script>

The widget renders a complete signup form right where the script tag is placed.

Configuration

Customize the widget with data- attributes on the script tag:

Attribute Required Default Description
data-waitlist-id Yes Your waitlist ID (UUID, e.g., a1b2c3d4-e5f6-7890-abcd-ef1234567890)
data-theme No "dark" "dark", "light", "glass" (dark with blur effect), or "auto" (follows system preference)
data-variant No "full" Widget layout variant: "full" (default), "docked", or "mini"
data-api-url No https://makeemwait.com/api/v1 Override the API base URL
data-compact No Set to "true" for compact single-row layout

Example with Light Theme

<script src="https://makeemwait.com/assets/js/embed.js"
        data-waitlist-id="a1b2c3d4"
        data-theme="light"></script>

Themes

The data-theme attribute controls the widget’s color scheme:

  • dark (default) — dark background with light text, suited for dark-themed sites
  • light — white background with dark text, suited for light-themed sites
  • glass — dark semi-transparent background with a 16px blur effect and a subtle indigo glow. Looks best when placed over an image or gradient background.
  • auto — follows the visitor’s system color scheme preference via the prefers-color-scheme media query. Displays the light theme when the system is in light mode, and the dark theme when the system is in dark mode. This is the best choice when your site also adapts to the system preference.

Compact Mode

Set data-compact="true" to display the email input and submit button on the same row (side-by-side). This creates a more space-efficient layout that works well in tight spaces like headers, sidebars, and inline call-to-action sections. When the waitlist has name fields enabled, they appear below the compact row.

<script src="https://makeemwait.com/assets/js/embed.js"
        data-waitlist-id="a1b2c3d4"
        data-compact="true"></script>

You can combine compact mode with any theme:

<script src="https://makeemwait.com/assets/js/embed.js"
        data-waitlist-id="a1b2c3d4"
        data-theme="auto"
        data-compact="true"></script>

How It Works

The embed widget:

  1. Creates a Shadow DOM container at the script tag’s location
  2. Fetches your waitlist config (headline, subheadline, field settings, custom questions)
  3. Renders the signup form with all your configured settings
  4. Handles submissions directly to the MakeEmWait API
  5. Shows the success state with position number after signup

Shadow DOM Isolation

The widget uses Shadow DOM (attachShadow({ mode: "open" })) to encapsulate all styles and markup. This means:

  • Your site’s CSS cannot break the widget’s appearance
  • The widget’s styles cannot leak into your page
  • It works reliably on any site regardless of CSS framework
*
Shadow DOM means you don't need to worry about CSS conflicts. The widget looks the same on every site.

Features

The embed widget automatically supports:

  • Email field (always shown)
  • Name fields (shown based on your waitlist settings)
  • Phone field — rendered based on the waitlist’s phone_field setting (hidden, optional, or required)
  • Custom questions — renders custom questions from the waitlist config, including text inputs, dropdowns, and checkboxes
  • Consent checkbox — when require_consent is enabled, shows an unchecked checkbox with your consent text. The submit button is disabled until the checkbox is checked. Supports markdown-style links in the consent text.
  • Social proof — when show_social_proof is enabled (default), displays a message like “Hundreds are already in line” above the form based on the current signup count
  • Closed waitlist — when the waitlist status is "closed", displays a “This waitlist is currently closed” message instead of the form
  • UTM passthrough — reads UTM parameters from the parent page URL and includes them with the signup
  • Referral tracking — reads the ref parameter from the parent page URL and passes it as referred_by_token in the signup request, so referrals from shared links are tracked automatically
  • Custom redirect URL — if the waitlist has a redirect URL configured, the widget redirects to it after signup with position, referral_token, and total_signups as query parameters
!
Advanced Plan Required Custom redirect URLs require an Advanced plan or higher.
  • Success state — shows “You’re on the list!” with position number, referral link, and social sharing buttons (when no custom redirect URL is set)
  • Social sharing — after signup, the success state shows sharing buttons for X/Twitter, LinkedIn, and a copy-link button with pre-filled share text
  • Returning visitor detection — returning visitors who previously signed up on this waitlist automatically see a “Welcome back!” screen with their current position and referral link instead of the signup form. A “Not you? Sign up” link lets them switch back to the form.
  • Error handling — displays inline error messages
  • Branding — shows “Powered by MakeEmWait” (hidden if branding is disabled)
!
Advanced Plan Required Hiding branding and hiding position numbers require an Advanced plan or higher.
  • Responsive — adjusts to the container width with a max-width of 400px

Returning Visitor Detection

When someone signs up through embed.js, their email is saved in the browser’s localStorage under the key mew_embed_{waitlistId}. On subsequent visits, the widget detects the stored email and shows a “Welcome back!” screen instead of the signup form. The welcome-back screen displays:

  • Their current position in line (fetched live from the API)
  • Their referral link
  • Social sharing buttons (X/Twitter, LinkedIn, Copy Link)
  • A “Not you? Sign up” link that clears the stored email and shows the form again

This only applies to embed.js. The embed-plain.js variant does not store signup data or detect returning visitors.

Social Sharing

On successful signup (and on the welcome-back screen), embed.js displays sharing buttons for:

  • X (Twitter) — opens a tweet composer with a pre-filled message and the referral link
  • LinkedIn — opens the LinkedIn share dialog with the referral link
  • Copy Link — copies the referral link to the clipboard with a “Copied!” confirmation

The share text includes the waitlist name when available (for example, “I just joined the Acme waitlist! Join too:”).

This only applies to embed.js. The embed-plain.js variant does not include social sharing buttons.

UTM Passthrough

The widget automatically reads UTM parameters from the parent page’s URL. If your page URL is:

https://yoursite.com/landing?utm_source=twitter&utm_campaign=launch

Those UTM values are included with every signup from the widget, so you can track which campaigns drive signups.

Cross-Origin Behavior

The widget works from any origin without CORS issues. All public API endpoints (/public/*) return Access-Control-Allow-Origin: *, so you can embed the script on any domain — your marketing site, a Webflow landing page, a Notion page, or anywhere else that allows custom script tags.

Placement Tips

  • Place the script tag where you want the form to appear in the page flow
  • The widget has a max-width of 400px and centers within its container
  • It works inside columns, modals, sidebars, or any other layout
  • Multiple widgets on the same page are supported (use different waitlist IDs)
i
The embed code is available from the dashboard. Click 'Embed' on any waitlist card to copy it to your clipboard.

Widget Style Customization

!
Advanced Plan Required Custom widget styling requires an Advanced plan or higher.

Beyond the built-in themes, you can fully customize the embed widget’s appearance using the widget_style object in your waitlist settings. Custom styles are applied within the Shadow DOM, so they override the theme defaults without affecting your host page.

API: PATCH /waitlists/{waitlist_id}

{
  "settings": {
    "widget_style": {
      "background_color": "#1A1A2E",
      "text_color": "#E0E0E0",
      "button_color": "#6366F1",
      "button_text_color": "#FFFFFF",
      "accent_color": "#818CF8",
      "input_background_color": "#16213E",
      "border_color": "#334155",
      "border_radius": 12,
      "font_family": "Inter",
      "logo_url": "https://example.com/logo.png"
    }
  }
}

Available Style Properties

Property Type Description
background_color hex color (e.g., #1A1A2E) Widget background color
text_color hex color Primary text color (labels, headings)
button_color hex color Submit button background color
button_text_color hex color Submit button text color
accent_color hex color Accent color for focus states and highlights
input_background_color hex color Background color for input fields
border_color hex color Border color for inputs and the widget container
border_radius number (0-24) Border radius in pixels for the widget container, inputs, and button
font_family string One of: Inter, Roboto, Open Sans, Lato, Montserrat, Poppins, Source Sans Pro, Nunito, Raleway, system-ui
logo_url HTTPS URL or null URL of a logo image displayed in the widget. Must start with https://.

All color values must be valid hex colors in the format #XXXXXX. The border_radius is a number (not a CSS string) between 0 and 24.

Custom styles are merged with the selected theme. Properties you set override the theme defaults; properties you omit use the theme’s values. This means you can start from a theme and tweak individual properties.

Example: Branded Widget

{
  "settings": {
    "widget_style": {
      "button_color": "#FF6B00",
      "button_text_color": "#FFFFFF",
      "border_radius": 24
    }
  }
}

This keeps the rest of the dark theme defaults but gives the widget an orange button with rounder corners.

*
Widget style customization applies to the styled embed.js variant only. The unstyled embed-plain.js has no built-in styles, so you control everything with your own CSS.

Unstyled Variant: embed-plain.js

For full control over the form’s appearance, use embed-plain.js. It renders plain HTML with no Shadow DOM and no injected CSS. You provide all the styling yourself.

Quick Start

<script src="https://makeemwait.com/assets/js/embed-plain.js"
        data-waitlist-id="YOUR_WAITLIST_ID"></script>

Configuration

embed-plain.js supports only two attributes:

Attribute Required Default Description
data-waitlist-id Yes Your waitlist ID (UUID, e.g., a1b2c3d4-e5f6-7890-abcd-ef1234567890)
data-api-url No https://makeemwait.com/api/v1 Override the API base URL

There is no data-theme or data-compact attribute. Since there are no injected styles, themes are not applicable — you write all the CSS yourself.

How It Differs from embed.js

Feature embed.js embed-plain.js
Shadow DOM Yes No
Injected CSS Yes (built-in themes) No (you provide all CSS)
Theme support dark, light, glass, auto None
Compact mode Yes (data-compact) No
Returning visitor detection Yes (localStorage) No
Social sharing buttons Yes (X, LinkedIn, Copy Link) No
Social proof text Yes No
Closed waitlist message No dedicated message No
Consent checkbox Yes No
Style isolation Full (Shadow DOM) None (host page CSS applies)
Custom questions Yes Yes
UTM passthrough Yes Yes
Referral tracking Yes Yes
Honeypot bot protection Yes Yes
Custom redirect URL Yes Yes
Branding (“Powered by”) Yes Yes

No Shadow DOM

Because embed-plain.js does not use Shadow DOM, styles from the host page will apply to the widget’s elements. This is intentional — it lets the form inherit your site’s typography, colors, spacing, and overall design. It also means you need to style the form yourself, or it will appear as unstyled browser-default HTML.

Element IDs

All element IDs are suffixed with the waitlist ID to avoid conflicts when multiple forms exist on the same page. For example, if your waitlist ID is abc123:

  • mew-email-abc123
  • mew-first-abc123
  • mew-last-abc123
  • mew-phone-abc123
  • mew-submit-abc123
  • mew-error-abc123
  • mew-success-abc123
  • mew-branding-abc123

CSS Classes Reference

These are the class names rendered by embed-plain.js. Use them in your CSS to style the form.

Class Element Description
.mew-embed <div> Outer container wrapping the entire widget
.mew-form-section <div> Wrapper around the form (hidden on success)
.mew-form <form> The form element itself
.mew-field <div> Wrapper around each individual field (label + input pair)
.mew-label <label> Field labels (Email, First name, Last name, Phone, custom question labels)
.mew-input <input> Text, email, and tel input fields
.mew-select <select> Dropdown select fields (for dropdown-type custom questions)
.mew-name-row <div> Wrapper around the first name and last name fields. Contains two .mew-field children. Style with flexbox or grid for side-by-side layout.
.mew-checkbox-field <div> Wrapper around checkbox-type custom questions (contains a checkbox input and label)
.mew-checkbox <input> Checkbox inputs for checkbox-type custom questions
.mew-submit <button> The submit button
.mew-error <div> Error message container (hidden by default, shown when submission fails)
.mew-success <div> Success state wrapper (hidden by default, shown after successful signup)
.mew-branding <div> “Powered by MakeEmWait” link (hidden on Advanced+ with branding disabled)

Example Styling

Here is a minimal example that styles the plain embed to match a clean, light form design:

<style>
  .mew-embed {
    max-width: 400px;
    font-family: system-ui, sans-serif;
  }
  .mew-field {
    margin-bottom: 0.75rem;
  }
  .mew-label {
    display: block;
    font-size: 0.875rem;
    font-weight: 500;
    margin-bottom: 0.25rem;
  }
  .mew-input, .mew-select {
    width: 100%;
    padding: 0.5rem 0.75rem;
    border: 1px solid #d1d5db;
    border-radius: 6px;
    font-size: 0.9rem;
    box-sizing: border-box;
  }
  .mew-input:focus, .mew-select:focus {
    outline: none;
    border-color: #6366f1;
    box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.15);
  }
  .mew-name-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.75rem;
  }
  .mew-checkbox-field {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
  }
  .mew-submit {
    width: 100%;
    padding: 0.625rem;
    background: #6366f1;
    color: #fff;
    border: none;
    border-radius: 6px;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
  }
  .mew-submit:hover {
    background: #818cf8;
  }
  .mew-submit:disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }
  .mew-error {
    color: #ef4444;
    font-size: 0.8rem;
    margin-top: 0.5rem;
  }
  .mew-success {
    text-align: center;
    padding: 1rem 0;
  }
  .mew-branding {
    text-align: center;
    font-size: 0.7rem;
    margin-top: 0.75rem;
  }
  .mew-branding a {
    color: #9ca3af;
    text-decoration: none;
  }
</style>

<script src="https://makeemwait.com/assets/js/embed-plain.js"
        data-waitlist-id="a1b2c3d4"></script>
*
embed-plain.js is ideal when you need the form to match your site's existing design system, or when you're building inside a framework like Tailwind CSS where you want full control over every class.