Newsletter Signup Form

A focused single-field email signup. Zero friction, high conversion. Works with any site.

Before you start
Before using the Form Template below, you need a Formtorch endpoint.
Don't have one yet? Sign up now and get one in less than 5 minutes.

Add your Formtorch endpoint

Paste your form ID and the code below updates instantly.

newsletter.html
<section class="ft-newsletter-demo" aria-label="Newsletter signup form demo">
  <div class="ft-window">
    <div class="ft-window-bar" aria-hidden="true">
      <span></span>
      <span></span>
      <span></span>
    </div>

    <form id="newsletter-form" class="ft-form" accept-charset="UTF-8">
      <div class="ft-icon" aria-hidden="true">
        <svg viewBox="0 0 24 24">
          <path d="M4 6h16v12H4z" />
          <path d="m4 7 8 6 8-6" />
        </svg>
      </div>

      <div class="ft-form-heading">
        <h2>Stay in the loop</h2>
        <p>
          Get the latest updates, product news, and exclusive tips, straight to
          your inbox.
        </p>
      </div>

      <input type="hidden" name="_formName" value="Newsletter Signup" />

      <div class="ft-field">
        <div class="ft-control">
          <svg aria-hidden="true" viewBox="0 0 24 24">
            <path d="M4 6h16v12H4z" />
            <path d="m4 7 8 6 8-6" />
          </svg>
          <input
            id="ft-email"
            type="email"
            name="email"
            placeholder="Enter your email"
            aria-label="Email address"
            autocomplete="email"
            required
          />
        </div>
      </div>

      <button class="ft-submit" type="submit">
        <svg aria-hidden="true" viewBox="0 0 24 24">
          <path d="m22 2-7 20-4-9-9-4Z" />
          <path d="M22 2 11 13" />
        </svg>
        <span>Join Newsletter</span>
      </button>

      <p id="ft-status" class="ft-status" aria-live="polite"></p>
    </form>
  </div>
</section>

<style>
  html,
  body {
    margin: 0;
    padding: 0;
    background: #05070d;
  }

  .ft-newsletter-demo {
    position: relative;
    box-sizing: border-box;
    width: 100%;
    min-height: 100vh;
    padding: clamp(24px, 5vw, 64px);
    display: grid;
    place-items: center;
    font-family:
      Inter,
      ui-sans-serif,
      system-ui,
      -apple-system,
      BlinkMacSystemFont,
      "Segoe UI",
      sans-serif;
    color: #f8fafc;
    background:
      radial-gradient(circle at 22% 18%, rgb(37 99 235 / 0.2), transparent 32%),
      radial-gradient(
        circle at 78% 75%,
        rgb(37 99 235 / 0.18),
        transparent 30%
      ),
      #05070d;
  }

  .ft-newsletter-demo,
  .ft-newsletter-demo * {
    box-sizing: border-box;
  }

  .ft-newsletter-demo::before,
  .ft-newsletter-demo::after {
    content: "";
    position: absolute;
    z-index: 0;
    width: clamp(90px, 16vw, 130px);
    height: clamp(130px, 20vw, 190px);
    pointer-events: none;
    opacity: 0.45;
    background-image: radial-gradient(
      rgb(37 99 235 / 0.9) 1.5px,
      transparent 1.5px
    );
    background-size: 20px 20px;
  }

  .ft-newsletter-demo::before {
    top: 9%;
    left: 5%;
  }

  .ft-newsletter-demo::after {
    bottom: 9%;
    right: 5%;
  }

  .ft-window {
    position: relative;
    z-index: 1;
    width: min(100%, 460px);
    overflow: hidden;
    border: 1px solid rgb(148 163 184 / 0.22);
    border-radius: 24px;
    background:
      linear-gradient(180deg, rgb(255 255 255 / 0.06), transparent 130px),
      linear-gradient(145deg, rgb(15 23 42 / 0.96), rgb(2 6 23 / 0.96));
    box-shadow:
      0 26px 80px rgb(0 0 0 / 0.45),
      0 0 0 1px rgb(255 255 255 / 0.04),
      0 0 44px rgb(37 99 235 / 0.2);
  }

  .ft-window-bar {
    position: relative;
    z-index: 1;
    height: 64px;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 32px;
    border-bottom: 1px solid rgb(148 163 184 / 0.12);
    background: rgb(255 255 255 / 0.025);
  }

  .ft-window-bar span {
    width: 14px;
    height: 14px;
    border-radius: 999px;
    background: #2563eb;
    box-shadow: 0 0 20px rgb(37 99 235 / 0.55);
  }

  .ft-window-bar span:nth-child(2) {
    background: #1d4ed8;
  }

  .ft-window-bar span:nth-child(3) {
    background: #334155;
    box-shadow: none;
  }

  .ft-form {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 20px;
    padding: clamp(32px, 6vw, 44px);
    text-align: center;
  }

  .ft-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 60px;
    height: 60px;
    border-radius: 16px;
    background: linear-gradient(
      160deg,
      rgb(37 99 235 / 0.22),
      rgb(15 23 42 / 0.4)
    );
    border: 1px solid rgb(148 163 184 / 0.18);
    box-shadow: 0 0 30px rgb(37 99 235 / 0.18);
  }

  .ft-icon svg {
    width: 26px;
    height: 26px;
    color: #3b82f6;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  .ft-form-heading h2 {
    margin: 0;
    color: #f8fafc;
    font-size: clamp(26px, 5vw, 32px);
    line-height: 1.15;
    letter-spacing: -0.03em;
  }

  .ft-form-heading p {
    margin: 10px 0 0;
    color: #94a3b8;
    font-size: clamp(14px, 2.4vw, 15px);
    line-height: 1.55;
    max-width: 320px;
  }

  .ft-field {
    width: 100%;
  }

  .ft-control {
    position: relative;
    display: flex;
    align-items: center;
  }

  .ft-control svg {
    position: absolute;
    left: 18px;
    width: 20px;
    height: 20px;
    color: #2563eb;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    pointer-events: none;
  }

  .ft-control input {
    width: 100%;
    height: 54px;
    border: 1px solid rgb(148 163 184 / 0.24);
    border-radius: 10px;
    background: rgb(15 23 42 / 0.52);
    color: #f8fafc;
    font: inherit;
    font-size: 16px;
    padding: 0 18px 0 56px;
    outline: none;
    transition:
      border-color 160ms ease,
      background-color 160ms ease,
      box-shadow 160ms ease;
  }

  .ft-control input::placeholder {
    color: #64748b;
  }

  .ft-control input:hover {
    border-color: rgb(148 163 184 / 0.38);
    background: rgb(15 23 42 / 0.7);
  }

  .ft-control input:focus-visible {
    border-color: #2563eb;
    background: rgb(15 23 42 / 0.78);
    box-shadow: 0 0 0 4px rgb(37 99 235 / 0.18);
  }

  .ft-submit {
    appearance: none;
    width: 100%;
    height: 56px;
    border: 0;
    border-radius: 10px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 0 22px;
    background: linear-gradient(135deg, #3b82f6, #1d4ed8);
    color: #fff;
    font: inherit;
    font-size: 16px;
    font-weight: 650;
    cursor: pointer;
    box-shadow:
      inset 0 1px 0 rgb(255 255 255 / 0.2),
      0 16px 34px rgb(37 99 235 / 0.18);
    transition:
      transform 160ms ease,
      filter 160ms ease,
      box-shadow 160ms ease;
  }

  .ft-submit svg {
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  .ft-submit:hover {
    filter: brightness(1.05);
    box-shadow:
      inset 0 1px 0 rgb(255 255 255 / 0.22),
      0 20px 42px rgb(37 99 235 / 0.24);
  }

  .ft-submit:active {
    transform: translateY(1px);
  }

  .ft-submit:focus-visible {
    outline: none;
    box-shadow:
      0 0 0 4px rgb(37 99 235 / 0.22),
      0 20px 42px rgb(37 99 235 / 0.24);
  }

  .ft-submit:disabled {
    cursor: not-allowed;
    opacity: 0.65;
    filter: grayscale(0.15);
  }

  .ft-status {
    min-height: 24px;
    margin: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    color: #94a3b8;
    font-size: 14px;
    line-height: 1.4;
  }

  .ft-status svg {
    width: 17px;
    height: 17px;
    flex: 0 0 auto;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  .ft-status[data-state="success"] {
    color: #22c55e;
  }

  .ft-status[data-state="error"] {
    color: #f87171;
  }

  @media (max-width: 480px) {
    .ft-newsletter-demo {
      padding: 18px;
    }

    .ft-newsletter-demo::before,
    .ft-newsletter-demo::after {
      display: none;
    }

    .ft-window {
      border-radius: 20px;
    }

    .ft-window-bar {
      height: 54px;
      padding: 0 22px;
    }

    .ft-form {
      gap: 16px;
      padding: 28px 22px;
    }

    .ft-control input {
      height: 50px;
    }

    .ft-submit {
      height: 52px;
    }
  }
</style>

<script>
  const form = document.getElementById("newsletter-form");
  const status = document.getElementById("ft-status");
  const button = form.querySelector("button[type='submit']");
  const buttonLabel = button.querySelector("span");

  const successIcon =
    '<svg aria-hidden="true" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg>';
  const errorIcon =
    '<svg aria-hidden="true" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"></circle><path d="m15 9-6 6"></path><path d="m9 9 6 6"></path></svg>';

  form.addEventListener("submit", async (event) => {
    event.preventDefault();

    status.innerHTML = "";
    status.removeAttribute("data-state");
    button.disabled = true;
    buttonLabel.textContent = "Joining...";

    try {
      const response = await fetch("https://formtorch.com/f/abc123", {
        method: "POST",
        headers: {
          "X-Requested-With": "XMLHttpRequest",
          Accept: "application/json",
        },
        body: new FormData(form),
      });

      if (!response.ok) {
        throw new Error("Submission failed");
      }

      form.reset();
      status.dataset.state = "success";
      status.innerHTML =
        successIcon + "<span>Thanks! You'll be the first to know.</span>";
      buttonLabel.textContent = "Join Newsletter";
    } catch {
      status.dataset.state = "error";
      status.innerHTML =
        errorIcon + "<span>Something went wrong. Please try again.</span>";
      buttonLabel.textContent = "Try again";
    } finally {
      button.disabled = false;
    }
  });
</script>

Why Use This Template

What is a newsletter signup form?

A newsletter signup form is a single email input that collects a visitor's address so you can add them to your mailing list. It's deliberately narrower than a full contact form: one field, one action, nothing else to fill in.

Why do you need a newsletter signup form?

Every extra field on a signup form is a chance for a visitor to abandon it before subscribing. A one-field form removes that friction and captures the one piece of information you actually need: the email address.

How do you add a newsletter signup form with a backend like Formtorch?

Paste the markup into your page and point the action attribute at your Formtorch endpoint: there's no signup API or database to build. Forward each submission to Mailchimp, ConvertKit, or any list manager with a Formtorch webhook to handle sending and unsubscribes.

Related read: how to route form submissions into email notifications

How it works

Create a Formtorch endpoint

Sign up and create a new form endpoint in the dashboard. You will get a unique URL in seconds.

Create endpoint

Paste it into your form

Set your form's action attribute to your endpoint URL. No other changes required.

Deploy and start receiving submissions

Every submission is stored in your dashboard, triggers email notifications, and can fire webhooks to any service.

Frequently asked questions

No. Formtorch works with standard HTML forms via the action attribute. No JavaScript required for basic submissions.

Start in less than 5 minutes

Create your endpoint, paste it into any template, and you are live.