React

Ship waitlist features faster in your React apps

Developer-first waitlist integration. Drop-in components, TypeScript support, and full control over your UI. Build production-ready waitlist flows in minutes, not hours.

React + Waitlister

Trusted by 2,000+
businesses & entrepreneurs

Data Hokage logo
Data Hokage
Fink Academy logo
Fink Academy
stagewise logo
stagewise
Sirius AI logo
Sirius AI
BLADNA logo
BLADNA
PagePal logo
PagePal
ChatAce.io logo
ChatAce.io
Instanote logo
Instanote
DirectoryDeck logo
DirectoryDeck
landman® logo
landman®
datapro logo
datapro
NATRU logo
NATRU
Pop Date logo
Pop Date
Aspire logo
Aspire
WalletX logo
WalletX
quickblogs logo
quickblogs
Data Hokage logo
Data Hokage
Fink Academy logo
Fink Academy
stagewise logo
stagewise
Sirius AI logo
Sirius AI
BLADNA logo
BLADNA
PagePal logo
PagePal
ChatAce.io logo
ChatAce.io
Instanote logo
Instanote
DirectoryDeck logo
DirectoryDeck
landman® logo
landman®
datapro logo
datapro
NATRU logo
NATRU
Pop Date logo
Pop Date
Aspire logo
Aspire
WalletX logo
WalletX
quickblogs logo
quickblogs
“Waitlister has been amazing; honestly, I don't plan on changing to another provider. Being able to create beautiful landing pages for my waitlist has been amazing. Customer support's response time is amazing, which has helped me deploy quickly.”
Data Hokage logo
Sinazo Bogicevic
Founder, Data Hokage
Use Cases

What you can build

Popular ways React users implement waitlists

SaaS Product Launches

Build conversion-optimized waitlist pages for your SaaS MVP with custom React components.

Example: Dashboard preview with inline waitlist modal triggered by CTA

Developer Tool Pre-Releases

Collect early adopter signups for APIs, CLI tools, or developer products with technical landing pages.

Example: API documentation site with beta access waitlist integration

Feature Beta Programs

Roll out new features gradually with waitlisted access. Manage beta user onboarding programmatically.

Example: In-app "Request Beta Access" button for experimental features

Web3 & Crypto Projects

Build waitlists for NFT drops, token launches, or web3 applications with React + Web3 integrations.

Example: NFT mint page with wallet-connected waitlist registration

Consumer App Launches

Launch consumer-facing React apps with built-in waitlist systems and viral referral mechanics.

Example: Mobile-first PWA with waitlist and referral position tracking

Analytics Dashboard Access

Gate premium features or enterprise tiers behind waitlisted access with React components.

Example: Free tier users can join waitlist for advanced analytics features
Benefits

Why Waitlister for React?

Built to work seamlessly with React's capabilities

Component-First Architecture

Build custom waitlist UIs with React components. Full control over markup, styling, and behavior. Integrate seamlessly with your component library and design system.

TypeScript Native Support

First-class TypeScript support with full type definitions. Autocomplete, type safety, and IntelliSense for all integration methods. Zero any types.

State Management Ready

Works with Redux, Zustand, Jotai, or any state library. Handle form state, loading states, and error handling your way. Examples provided for popular libraries.

Production-Grade Performance

Optimized for React 18+ with automatic batching. Lazy loading support, code splitting compatible, and bundle size conscious. Under 2KB for embed widget.

Form Library Compatible

Integrate with React Hook Form, Formik, or uncontrolled forms. Built-in validation patterns. Works with your existing form infrastructure.

Developer Experience First

Clear error messages, detailed logging in dev mode, comprehensive examples. Built by developers, for developers. Extensive TypeScript examples in docs.

Choose Your Method

Which integration is
right for you?

Compare both methods to find the best fit for your React project

FeatureForm ActionEmbeddable Widget
Setup ComplexityModerate (custom form)Simple (one component)
UI ControlComplete controlLimited to styling
TypeScript SupportFull type safetyBasic
State ManagementYour choiceInternal only
Form ValidationCustom logicBuilt-in
Bundle Impact0KB (native fetch)~2KB (async)
Best ForProduction appsQuick MVPs

Choose Form Action if...

  • You need pixel-perfect control over every element
  • You're integrating with existing form libraries
  • You want type-safe forms with TypeScript
  • You need custom validation or complex logic
  • You're building for production at scale
  • You want complete control over state management

Choose Embeddable Widget if...

  • You need a working waitlist in under 10 minutes
  • You're building an MVP or prototype quickly
  • You don't need deep customization
  • You want a ready-made UI component
  • Your design system is flexible
Step-by-Step Guide

How to integrate

Follow these React-specific instructions

1

Get your Waitlister API endpoint

Sign into Waitlister → Overview → copy your waitlist key. Your submission endpoint is:
https://waitlister.me/s/YOUR_WAITLIST_KEY

Pro tip
Store this in an environment variable: REACT_APP_WAITLIST_KEY or VITE_WAITLIST_KEY
2

Create a custom waitlist form component

Build a form component with React state management:

// components/WaitlistForm.jsx
import { useState } from 'react';

export default function WaitlistForm() {
  const [email, setEmail] = useState('');
  const [name, setName] = useState('');
  const [loading, setLoading] = useState(false);
  const [success, setSuccess] = useState(false);
  const [error, setError] = useState(null);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError(null);

    try {
      const response = await fetch('https://waitlister.me/s/YOUR_WAITLIST_KEY', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: new URLSearchParams({ email, name })
      });

      if (response.ok) {
        setSuccess(true);
        setEmail('');
        setName('');
      } else {
        throw new Error('Submission failed');
      }
    } catch (err) {
      setError('Failed to join waitlist. Please try again.');
    } finally {
      setLoading(false);
    }
  };

  if (success) {
    return <div className="success">Thanks! You're on the list.</div>;
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Your email"
        required
      />
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Your name (optional)"
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Joining...' : 'Join Waitlist'}
      </button>
      {error && <div className="error">{error}</div>}
    </form>
  );
}
Pro tip
This gives you complete control over the form UI and behavior
3

TypeScript version with full types

Here's a production-ready TypeScript version:

// components/WaitlistForm.tsx
import { useState, FormEvent, ChangeEvent } from 'react';

interface FormData {
  email: string;
  name: string;
}

interface WaitlistFormProps {
  onSuccess?: () => void;
  className?: string;
}

export default function WaitlistForm({ onSuccess, className }: WaitlistFormProps) {
  const [formData, setFormData] = useState<FormData>({ email: '', name: '' });
  const [loading, setLoading] = useState<boolean>(false);
  const [success, setSuccess] = useState<boolean>(false);
  const [error, setError] = useState<string | null>(null);

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    setFormData(prev => ({ ...prev, [e.target.name]: e.target.value }));
  };

  const handleSubmit = async (e: FormEvent) => {
    e.preventDefault();
    setLoading(true);
    setError(null);

    try {
      const response = await fetch(
        `https://waitlister.me/s/${process.env.REACT_APP_WAITLIST_KEY}`,
        {
          method: 'POST',
          headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
          body: new URLSearchParams(formData)
        }
      );

      if (response.ok) {
        setSuccess(true);
        setFormData({ email: '', name: '' });
        onSuccess?.();
      } else {
        throw new Error('Submission failed');
      }
    } catch (err) {
      setError('Failed to join waitlist. Please try again.');
    } finally {
      setLoading(false);
    }
  };

  if (success) {
    return (
      <div className="success">
        <h3>You're on the list!</h3>
        <p>We'll notify you when we launch.</p>
      </div>
    );
  }

  return (
    <form onSubmit={handleSubmit} className={className}>
      <input
        type="email"
        name="email"
        value={formData.email}
        onChange={handleChange}
        placeholder="Your email"
        required
      />
      <input
        type="text"
        name="name"
        value={formData.name}
        onChange={handleChange}
        placeholder="Your name (optional)"
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Joining...' : 'Join Waitlist'}
      </button>
      {error && <div className="error">{error}</div>}
    </form>
  );
}
4

Integrate with React Hook Form (recommended)

For production apps, use React Hook Form for better validation and UX:

// npm install react-hook-form
import { useForm } from 'react-hook-form';

type FormData = {
  email: string;
  name?: string;
};

export default function WaitlistForm() {
  const { register, handleSubmit, formState: { errors, isSubmitting }, reset } = useForm<FormData>();
  const [success, setSuccess] = useState(false);

  const onSubmit = async (data: FormData) => {
    const response = await fetch('https://waitlister.me/s/YOUR_KEY', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams(data as any)
    });

    if (response.ok) {
      setSuccess(true);
      reset();
    }
  };

  if (success) return <div>Success! You're on the list.</div>;

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register('email', {
          required: 'Email is required',
          pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
            message: 'Invalid email address'
          }
        })}
        placeholder="Your email"
      />
      {errors.email && <span>{errors.email.message}</span>}
      
      <input {...register('name')} placeholder="Your name (optional)" />
      
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Joining...' : 'Join Waitlist'}
      </button>
    </form>
  );
}
Pro tip
React Hook Form provides excellent DX with minimal re-renders and built-in validation.
5

Add custom fields and metadata

Pass additional data like UTM parameters, referral codes, or user context:

const handleSubmit = async (data: FormData) => {
  // Get URL parameters
  const urlParams = new URLSearchParams(window.location.search);
  
  const payload = {
    ...data,
    utm_source: urlParams.get('utm_source') || '',
    utm_campaign: urlParams.get('utm_campaign') || '',
    referral_code: urlParams.get('ref') || '',
    signup_page: window.location.pathname,
    user_agent: navigator.userAgent
  };

  await fetch('https://waitlister.me/s/YOUR_KEY', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams(payload)
  });
};
Pro tip
All fields beyond email, name, and phone are automatically saved as custom metadata in Waitlister.
6

Whitelist your React app domain

In Waitlister → Settings → Configure → Whitelisted domains, add:

Development: localhost:3000
Staging: staging.yourdomain.com
Production: yourdomain.com

For Vercel: your-app.vercel.app
For Netlify: your-app.netlify.app
Pro tip
Add all environments where you'll test and deploy your React app.
7

Test across your component tree

Import and test your form component in different routes and contexts. Verify state management, error handling, and success flows work correctly.

Need more details?

Check out our complete form action endpoint documentation.

View full documentation
Troubleshooting

Common issues & solutions

Quick fixes for React-specific problems

If you added the script to public/index.html, ensure it's before </body>. For SPAs, the script loads once on initial page load. If using React Router, the script persists across route changes automatically.

Waitlister API supports CORS for whitelisted domains. Ensure your domain (including localhost for dev) is added to Whitelist domains in Waitlister settings. Check browser console for specific domain that's being blocked.

Check browser Network tab for the API request. Verify the endpoint URL is correct, method is POST, and Content-Type is application/x-www-form-urlencoded. Enable error logging in your catch block.

Ensure the script tag in public/index.html is loaded before the component mounts. You can verify in browser DevTools → Network tab. The widget div needs the exact class name "waitlister-form" to be recognized.

URLSearchParams expects Record<string, string>. Cast your data or ensure all values are strings before passing to URLSearchParams constructor.

Make sure you're using the correct state setter syntax. For nested state, use functional updates: setFormData(prev => ({...prev, email: ''})) instead of direct mutation.

Add disabled={loading || isSubmitting} to your submit button to prevent double-clicks. Also ensure you're not calling the submit handler multiple times.

In Create React App, use REACT_APP_ prefix (REACT_APP_WAITLIST_KEY). In Vite, use VITE_ prefix (VITE_WAITLIST_KEY). Restart dev server after adding env variables.

If using styled-components or emotion, styles may conflict with the embed widget. Use className prop to add container styles, but don't try to style the internal .waitlister-form element.

FAQ

Common questions

About React integration

Yes! Works perfectly with Create React App, Vite, Next.js, Remix, and any React setup. The embed method uses a standard script tag. The form action method uses native fetch - no build tool specifics.

Absolutely! Full TypeScript support with complete type definitions in our examples. Type-safe forms, proper event handlers, and IntelliSense for all integration methods.

Yes! Fully compatible with React 18, including concurrent features, automatic batching, and Suspense. Also works with React 17 and 16.8+ (hooks required).

Yes! Works seamlessly with React Hook Form, Formik, or any form library. Use the custom form approach and integrate with your existing form infrastructure. Examples provided in integration guide.

Use React state (useState) to track loading, success, and error states. Show spinners, disable buttons during submission, and display error messages. Full examples provided in the form action integration steps.

Yes! The form action method gives you complete control. Dispatch Redux actions on success/failure, or update Zustand store. The embed method manages its own state internally.

Yes! Works perfectly with React Router (v5 and v6), Reach Router, or any routing solution. The embed script loads once and persists across route changes. Forms work on any route.

For custom forms (form action method), style however you want - Tailwind, styled-components, emotion, CSS modules, or plain CSS. For embed method, you can style the container but internal widget styling is managed in Waitlister dashboard.

Add localhost:3000 (or your dev port) to whitelisted domains in Waitlister settings. Forms will work exactly the same in development as production. Test all success/error flows thoroughly.

Yes! Both methods work in modals, drawers, popovers, or any overlay component. Just render the component inside your modal. The embed method auto-adjusts height for modal contexts.

Works great! For the embed method, load the script in _document.js or _app.js. For custom forms, use the form action method which works in both SSR and CSR contexts. Check our Next.js specific guide for details.

Yes! With the custom form approach, you have full control. Call your submit function from anywhere - button clicks, keyboard shortcuts, or other user interactions. Perfect for multi-step forms.

Explore

More integrations

Explore other platforms

Get started for free

Start collecting sign ups for your product launch in minutes — no coding required.