> ## Documentation Index
> Fetch the complete documentation index at: https://docs.elementum.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Unsubscribe from email updates

> Unsubscribe from Elementum General Availability Release notifications and Upcoming Feature updates.

export const EmailUnsubscribeForm = ({webhookUrl, logUrl = "https://script.google.com/macros/s/AKfycbwYKNyp9YTtV7fhwZOKwePB-0_cOz8jOD1kBLEprmvbTD5LBPn_iSuYagvaWlxbmtg/exec", heading = "Unsubscribe from email updates", description, buttonLabel = "Unsubscribe", lists = [{
  key: "ga-releases",
  label: "General Availability Release notifications"
}, {
  key: "upcoming-features",
  label: "Upcoming Feature updates"
}]}) => {
  const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const logSubmissionAttempt = payload => {
    if (!logUrl) return;
    try {
      fetch(logUrl, {
        method: "POST",
        mode: "no-cors",
        keepalive: true,
        headers: {
          "Content-Type": "text/plain;charset=UTF-8"
        },
        body: JSON.stringify(payload)
      }).catch(() => {});
    } catch (e) {}
  };
  const [email, setEmail] = useState("");
  const [selectedLists, setSelectedLists] = useState(() => lists.map(l => l.key));
  const [website, setWebsite] = useState("");
  const [status, setStatus] = useState("idle");
  const [errorMessage, setErrorMessage] = useState("");
  const isSubmitting = status === "loading";
  const [uid] = useState(() => `euf-${Math.random().toString(36).slice(2, 9)}`);
  const emailId = `${uid}-email`;
  const websiteId = `${uid}-website`;
  const statusId = `${uid}-status`;
  const toggleList = key => {
    setSelectedLists(prev => prev.includes(key) ? prev.filter(k => k !== key) : [...prev, key]);
  };
  const handleSubmit = async event => {
    event.preventDefault();
    setErrorMessage("");
    if (website.trim().length > 0) {
      setStatus("success");
      setEmail("");
      return;
    }
    const trimmedEmail = email.trim();
    if (!EMAIL_REGEX.test(trimmedEmail)) {
      setStatus("error");
      setErrorMessage("Please enter a valid email address.");
      return;
    }
    if (selectedLists.length === 0) {
      setStatus("error");
      setErrorMessage("Select at least one list to unsubscribe from.");
      return;
    }
    if (!webhookUrl) {
      setStatus("error");
      setErrorMessage("This form is not configured yet. Please try again later.");
      return;
    }
    setStatus("loading");
    const payload = {
      email: trimmedEmail,
      lists: selectedLists,
      action: "unsubscribe",
      source: typeof window !== "undefined" ? window.location.pathname : "",
      userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "",
      submittedAt: new Date().toISOString()
    };
    logSubmissionAttempt(payload);
    try {
      const response = await fetch(webhookUrl, {
        method: "POST",
        mode: "no-cors",
        headers: {
          "Content-Type": "text/plain;charset=UTF-8"
        },
        body: JSON.stringify(payload)
      });
      if (response.type !== "opaque" && !response.ok) {
        throw new Error(`Request failed with status ${response.status}`);
      }
      setStatus("success");
      setEmail("");
    } catch (err) {
      setStatus("error");
      setErrorMessage("We couldn't complete your request right now. Please try again in a moment.");
    }
  };
  const inputClass = "w-full rounded-md border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-900 placeholder:text-zinc-400 focus:border-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-600/30 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:placeholder:text-zinc-500";
  const labelClass = "block text-sm font-medium text-zinc-700 dark:text-zinc-200 mb-1";
  const checkboxLabelClass = "flex items-start gap-2 text-sm text-zinc-700 dark:text-zinc-200";
  const checkboxClass = "mt-0.5 h-4 w-4 rounded border-zinc-300 text-blue-600 focus:ring-blue-600/30 dark:border-zinc-700 dark:bg-zinc-900";
  return <div className="email-form not-prose my-4 rounded-xl border border-zinc-200 bg-zinc-50 p-5 dark:border-zinc-800 dark:bg-zinc-900/40">
      <h3 className="m-0 text-base font-semibold text-zinc-900 dark:text-zinc-50">
        {heading}
      </h3>
      {description ? <p className="mt-1 mb-0 text-sm text-zinc-600 dark:text-zinc-300">{description}</p> : null}

      <form onSubmit={handleSubmit} noValidate className="mt-4 flex flex-col gap-3">
        <div>
          <label htmlFor={emailId} className={labelClass}>
            Email
          </label>
          <input id={emailId} type="email" name="email" autoComplete="email" required value={email} onChange={e => setEmail(e.target.value)} disabled={isSubmitting} className={inputClass} placeholder="jane@acme.com" />
        </div>

        <fieldset className="mt-1 flex flex-col gap-2">
          <legend className={labelClass}>Unsubscribe me from:</legend>
          {lists.map(list => {
    const checkboxId = `${uid}-list-${list.key}`;
    return <label key={list.key} htmlFor={checkboxId} className={checkboxLabelClass}>
                <input id={checkboxId} type="checkbox" name="lists" value={list.key} checked={selectedLists.includes(list.key)} onChange={() => toggleList(list.key)} disabled={isSubmitting} className={checkboxClass} />
                <span>{list.label}</span>
              </label>;
  })}
        </fieldset>

        <div aria-hidden="true" style={{
    position: "absolute",
    left: "-10000px",
    top: "auto",
    width: "1px",
    height: "1px",
    overflow: "hidden"
  }}>
          <label htmlFor={websiteId}>Website (leave blank)</label>
          <input id={websiteId} type="text" name="website" tabIndex={-1} autoComplete="off" value={website} onChange={e => setWebsite(e.target.value)} />
        </div>

        <button type="submit" disabled={isSubmitting} className="mt-1 inline-flex w-full items-center justify-center rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60 dark:bg-blue-500 dark:hover:bg-blue-400">
          {isSubmitting ? "Unsubscribing…" : buttonLabel}
        </button>

        <div id={statusId} role="status" aria-live="polite" className="min-h-[1.25rem] text-sm">
          {status === "success" ? <span className="text-emerald-700 dark:text-emerald-400">
              You've been unsubscribed. It may take a few minutes to take effect.
            </span> : null}
          {status === "error" ? <span className="text-red-700 dark:text-red-400">{errorMessage}</span> : null}
        </div>
      </form>
    </div>;
};

Enter your email and select the lists you'd like to stop receiving. We'll remove you from the selected lists.

<EmailUnsubscribeForm webhookUrl="https://elementum.elementum.io/api/v1/webhooks/42096068-0baf-41b2-a4bb-276ac5122ddf" heading="Unsubscribe from email updates" description="Choose which lists to stop receiving and we'll take care of the rest." buttonLabel="Unsubscribe" />

If you change your mind later, you can [resubscribe from the Release Notes overview](/release-notes/overview#stay-in-the-loop).
