Newsletter signup form template
Email, first name, the topics they want, and consent that is a real choice.
Reviewed by Ritu Fadadu · Last reviewed
Preview and code
Preview
Code — replace YOUR_ENDPOINT_ID with your form's ID
<style>
.fb-form { display: grid; gap: 1rem; max-width: 36rem; }
.fb-field { display: flex; flex-direction: column; gap: 0.375rem; }
.fb-fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
.fb-label { font-weight: 600; }
.fb-optional { font-weight: 400; color: #6b7280; }
.fb-help { margin: 0; font-size: 0.875rem; color: #6b7280; }
.fb-input { box-sizing: border-box; width: 100%; padding: 0.625rem 0.75rem; border: 1px solid #d1d5db; border-radius: 0.5rem; background: #fff; color: #111827; font: inherit; }
.fb-input:focus { border-color: #2563eb; outline: 2px solid #2563eb; outline-offset: 1px; }
.fb-options { display: flex; flex-direction: column; gap: 0.5rem; }
.fb-option { display: flex; align-items: center; gap: 0.5rem; }
.fb-check { width: 1.125rem; height: 1.125rem; margin: 0; accent-color: #2563eb; }
.fb-button { justify-self: start; min-height: 44px; padding: 0.625rem 1.25rem; border: 0; border-radius: 0.5rem; background: #2563eb; color: #fff; font: inherit; font-weight: 600; cursor: pointer; }
.fb-button:hover { background: #1d4ed8; }
.fb-button:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }
.fb-button:disabled { opacity: 0.6; cursor: wait; }
.fb-status { margin: 0; min-height: 1.5em; }
@media (min-width: 640px) {
.fb-form { grid-template-columns: 1fr 1fr; }
.fb-field, .fb-button, .fb-status { grid-column: 1 / -1; }
.fb-field--half { grid-column: auto; }
}
</style>
<form id="fb-form" class="fb-form" action="https://formboost.app/f/YOUR_ENDPOINT_ID" method="POST">
<div class="fb-field">
<label for="fb-email" class="fb-label">Email</label>
<input type="email" id="fb-email" name="email" class="fb-input" required autocomplete="email">
</div>
<div class="fb-field fb-field--half">
<label for="fb-first_name" class="fb-label">
First name
<span class="fb-optional">(optional)</span>
</label>
<input
type="text"
id="fb-first_name"
name="first_name"
class="fb-input"
autocomplete="given-name"
>
</div>
<fieldset class="fb-field fb-fieldset">
<legend class="fb-label">
What would you like to hear about?
<span class="fb-optional">(optional)</span>
</legend>
<div class="fb-options">
<label class="fb-option">
<input type="checkbox" name="topics" value="product-updates" class="fb-check">
<span>Product updates</span>
</label>
<label class="fb-option">
<input type="checkbox" name="topics" value="tutorials" class="fb-check">
<span>Tutorials</span>
</label>
<label class="fb-option">
<input type="checkbox" name="topics" value="events" class="fb-check">
<span>Events</span>
</label>
</div>
</fieldset>
<div class="fb-field">
<label class="fb-option">
<input type="checkbox" id="fb-consent" name="consent" value="yes" class="fb-check" required>
<span>Email me the newsletter. I can unsubscribe at any time.</span>
</label>
</div>
<input type="hidden" name="source" value="footer">
<!-- Honeypot: keep it hidden. People never see it; bots fill it in and get flagged as spam. -->
<div
aria-hidden="true"
style="position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden"
>
<label for="fb-honey">Leave this field empty</label>
<input type="text" id="fb-honey" name="_honey" tabindex="-1" autocomplete="off">
</div>
<input type="hidden" name="_subject" value="New newsletter signup">
<button type="submit" class="fb-button">Subscribe</button>
</form>
<style>
.fb-form { display: grid; gap: 1rem; max-width: 36rem; }
.fb-field { display: flex; flex-direction: column; gap: 0.375rem; }
.fb-fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
.fb-label { font-weight: 600; }
.fb-optional { font-weight: 400; color: #6b7280; }
.fb-help { margin: 0; font-size: 0.875rem; color: #6b7280; }
.fb-input { box-sizing: border-box; width: 100%; padding: 0.625rem 0.75rem; border: 1px solid #d1d5db; border-radius: 0.5rem; background: #fff; color: #111827; font: inherit; }
.fb-input:focus { border-color: #2563eb; outline: 2px solid #2563eb; outline-offset: 1px; }
.fb-options { display: flex; flex-direction: column; gap: 0.5rem; }
.fb-option { display: flex; align-items: center; gap: 0.5rem; }
.fb-check { width: 1.125rem; height: 1.125rem; margin: 0; accent-color: #2563eb; }
.fb-button { justify-self: start; min-height: 44px; padding: 0.625rem 1.25rem; border: 0; border-radius: 0.5rem; background: #2563eb; color: #fff; font: inherit; font-weight: 600; cursor: pointer; }
.fb-button:hover { background: #1d4ed8; }
.fb-button:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }
.fb-button:disabled { opacity: 0.6; cursor: wait; }
.fb-status { margin: 0; min-height: 1.5em; }
@media (min-width: 640px) {
.fb-form { grid-template-columns: 1fr 1fr; }
.fb-field, .fb-button, .fb-status { grid-column: 1 / -1; }
.fb-field--half { grid-column: auto; }
}
</style>
<form id="fb-form" class="fb-form" action="https://formboost.app/f/YOUR_ENDPOINT_ID" method="POST">
<div class="fb-field">
<label for="fb-email" class="fb-label">Email</label>
<input type="email" id="fb-email" name="email" class="fb-input" required autocomplete="email">
</div>
<div class="fb-field fb-field--half">
<label for="fb-first_name" class="fb-label">
First name
<span class="fb-optional">(optional)</span>
</label>
<input
type="text"
id="fb-first_name"
name="first_name"
class="fb-input"
autocomplete="given-name"
>
</div>
<fieldset class="fb-field fb-fieldset">
<legend class="fb-label">
What would you like to hear about?
<span class="fb-optional">(optional)</span>
</legend>
<div class="fb-options">
<label class="fb-option">
<input type="checkbox" name="topics" value="product-updates" class="fb-check">
<span>Product updates</span>
</label>
<label class="fb-option">
<input type="checkbox" name="topics" value="tutorials" class="fb-check">
<span>Tutorials</span>
</label>
<label class="fb-option">
<input type="checkbox" name="topics" value="events" class="fb-check">
<span>Events</span>
</label>
</div>
</fieldset>
<div class="fb-field">
<label class="fb-option">
<input type="checkbox" id="fb-consent" name="consent" value="yes" class="fb-check" required>
<span>Email me the newsletter. I can unsubscribe at any time.</span>
</label>
</div>
<input type="hidden" name="source" value="footer">
<!-- Honeypot: keep it hidden. People never see it; bots fill it in and get flagged as spam. -->
<div
aria-hidden="true"
style="position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden"
>
<label for="fb-honey">Leave this field empty</label>
<input type="text" id="fb-honey" name="_honey" tabindex="-1" autocomplete="off">
</div>
<input type="hidden" name="_subject" value="New newsletter signup">
<button type="submit" class="fb-button">Subscribe</button>
<p class="fb-status" role="status" aria-live="polite" data-fb-status></p>
</form>
<script>
document.getElementById("fb-form").addEventListener("submit", async (event) => {
event.preventDefault();
const form = event.currentTarget;
const status = form.querySelector("[data-fb-status]");
const button = form.querySelector('button[type="submit"]');
const fields = new FormData(form);
// Formboost accepts JSON or url-encoded bodies, not multipart, so send JSON.
const data = Object.fromEntries(fields);
// Multiple-choice fields can hold several values; send them as arrays.
data.topics = fields.getAll("topics");
// Makes the notification email replyable; the address still shows in the field table.
if (data.email) data._replyto = data.email;
button.disabled = true;
status.textContent = "Sending…";
try {
const response = await fetch(form.action, {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify(data),
});
if (response.ok) {
form.reset();
status.textContent = "You're subscribed — thanks!";
} else if (response.status === 429) {
status.textContent = "Too many attempts. Please wait a minute and try again.";
} else {
status.textContent = "Something went wrong. Please try again.";
}
} catch {
status.textContent = "Could not send. Check your connection and try again.";
} finally {
button.disabled = false;
}
});
</script>
import { useState } from "react";
// Your form's endpoint, from the Formboost dashboard.
const ENDPOINT = "https://formboost.app/f/YOUR_ENDPOINT_ID";
export default function NewsletterSignupForm() {
const [sending, setSending] = useState(false);
const [message, setMessage] = useState("");
async function handleSubmit(event) {
event.preventDefault();
const form = event.currentTarget;
const fields = new FormData(form);
// Formboost accepts JSON or url-encoded bodies, not multipart, so send JSON.
const data = Object.fromEntries(fields);
// Multiple-choice fields can hold several values; send them as arrays.
data.topics = fields.getAll("topics");
// Makes the notification email replyable; the address still shows in the field table.
if (data.email) data._replyto = data.email;
setSending(true);
setMessage("Sending…");
try {
const response = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify(data),
});
if (response.ok) {
form.reset();
setMessage("You're subscribed — thanks!");
} else if (response.status === 429) {
setMessage("Too many attempts. Please wait a minute and try again.");
} else {
setMessage("Something went wrong. Please try again.");
}
} catch {
setMessage("Could not send. Check your connection and try again.");
} finally {
setSending(false);
}
}
return (
<form
className="grid max-w-xl gap-4 sm:grid-cols-2"
action={ENDPOINT}
method="POST"
onSubmit={handleSubmit}
>
<div className="flex flex-col gap-1.5 sm:col-span-2">
<label htmlFor="fb-email" className="text-sm font-semibold text-gray-900">Email</label>
<input
type="email"
id="fb-email"
name="email"
className="w-full rounded-lg border border-gray-300 bg-white px-3 py-2.5 text-gray-900 focus:border-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-600/30"
required
autoComplete="email"
/>
</div>
<div className="flex flex-col gap-1.5">
<label htmlFor="fb-first_name" className="text-sm font-semibold text-gray-900">
First name
<span className="font-normal text-gray-500">(optional)</span>
</label>
<input
type="text"
id="fb-first_name"
name="first_name"
className="w-full rounded-lg border border-gray-300 bg-white px-3 py-2.5 text-gray-900 focus:border-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-600/30"
autoComplete="given-name"
/>
</div>
<fieldset className="flex flex-col gap-1.5 sm:col-span-2 m-0 min-w-0 border-0 p-0">
<legend className="text-sm font-semibold text-gray-900">
What would you like to hear about?
<span className="font-normal text-gray-500">(optional)</span>
</legend>
<div className="flex flex-col gap-2">
<label className="flex items-center gap-2 text-gray-800">
<input
type="checkbox"
name="topics"
value="product-updates"
className="h-4 w-4 accent-blue-600"
/>
<span>Product updates</span>
</label>
<label className="flex items-center gap-2 text-gray-800">
<input
type="checkbox"
name="topics"
value="tutorials"
className="h-4 w-4 accent-blue-600"
/>
<span>Tutorials</span>
</label>
<label className="flex items-center gap-2 text-gray-800">
<input
type="checkbox"
name="topics"
value="events"
className="h-4 w-4 accent-blue-600"
/>
<span>Events</span>
</label>
</div>
</fieldset>
<div className="flex flex-col gap-1.5 sm:col-span-2">
<label className="flex items-center gap-2 text-gray-800">
<input
type="checkbox"
id="fb-consent"
name="consent"
value="yes"
className="h-4 w-4 accent-blue-600"
required
/>
<span>Email me the newsletter. I can unsubscribe at any time.</span>
</label>
</div>
<input type="hidden" name="source" defaultValue="footer" />
{/* Honeypot: keep it hidden. People never see it; bots fill it in and get flagged as spam. */}
<div
aria-hidden="true"
style={{ position: "absolute", left: "-9999px", width: "1px", height: "1px", overflow: "hidden" }}
>
<label htmlFor="fb-honey">Leave this field empty</label>
<input type="text" id="fb-honey" name="_honey" tabIndex={-1} autoComplete="off" />
</div>
<input type="hidden" name="_subject" defaultValue="New newsletter signup" />
<button
type="submit"
className="min-h-11 justify-self-start rounded-lg bg-blue-600 px-5 py-2.5 font-semibold text-white hover:bg-blue-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 disabled:opacity-60 sm:col-span-2"
disabled={sending}
>
Subscribe
</button>
<p className="min-h-6 text-sm text-gray-700 sm:col-span-2" role="status" aria-live="polite">
{message}
</p>
</form>
);
}
<form
id="fb-form"
class="grid max-w-xl gap-4 sm:grid-cols-2"
action="https://formboost.app/f/YOUR_ENDPOINT_ID"
method="POST"
>
<div class="flex flex-col gap-1.5 sm:col-span-2">
<label for="fb-email" class="text-sm font-semibold text-gray-900">Email</label>
<input
type="email"
id="fb-email"
name="email"
class="w-full rounded-lg border border-gray-300 bg-white px-3 py-2.5 text-gray-900 focus:border-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-600/30"
required
autocomplete="email"
>
</div>
<div class="flex flex-col gap-1.5">
<label for="fb-first_name" class="text-sm font-semibold text-gray-900">
First name
<span class="font-normal text-gray-500">(optional)</span>
</label>
<input
type="text"
id="fb-first_name"
name="first_name"
class="w-full rounded-lg border border-gray-300 bg-white px-3 py-2.5 text-gray-900 focus:border-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-600/30"
autocomplete="given-name"
>
</div>
<fieldset class="flex flex-col gap-1.5 sm:col-span-2 m-0 min-w-0 border-0 p-0">
<legend class="text-sm font-semibold text-gray-900">
What would you like to hear about?
<span class="font-normal text-gray-500">(optional)</span>
</legend>
<div class="flex flex-col gap-2">
<label class="flex items-center gap-2 text-gray-800">
<input
type="checkbox"
name="topics"
value="product-updates"
class="h-4 w-4 accent-blue-600"
>
<span>Product updates</span>
</label>
<label class="flex items-center gap-2 text-gray-800">
<input type="checkbox" name="topics" value="tutorials" class="h-4 w-4 accent-blue-600">
<span>Tutorials</span>
</label>
<label class="flex items-center gap-2 text-gray-800">
<input type="checkbox" name="topics" value="events" class="h-4 w-4 accent-blue-600">
<span>Events</span>
</label>
</div>
</fieldset>
<div class="flex flex-col gap-1.5 sm:col-span-2">
<label class="flex items-center gap-2 text-gray-800">
<input
type="checkbox"
id="fb-consent"
name="consent"
value="yes"
class="h-4 w-4 accent-blue-600"
required
>
<span>Email me the newsletter. I can unsubscribe at any time.</span>
</label>
</div>
<input type="hidden" name="source" value="footer">
<!-- Honeypot: keep it hidden. People never see it; bots fill it in and get flagged as spam. -->
<div
aria-hidden="true"
style="position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden"
>
<label for="fb-honey">Leave this field empty</label>
<input type="text" id="fb-honey" name="_honey" tabindex="-1" autocomplete="off">
</div>
<input type="hidden" name="_subject" value="New newsletter signup">
<button
type="submit"
class="min-h-11 justify-self-start rounded-lg bg-blue-600 px-5 py-2.5 font-semibold text-white hover:bg-blue-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 disabled:opacity-60 sm:col-span-2"
>
Subscribe
</button>
</form>
About this template
This is the form, not the mailing list. Formboost collects and stores each signup, emails you about it and screens out bots; it does not send newsletters or manage unsubscribes. That part belongs to your email tool, and on Starter and above you connect the two with a webhook, Zapier or n8n, so every new signup is added there automatically.
The consent checkbox is required and unticked by default, which is what makes the signup a deliberate opt-in. The topics question is optional multiple choice, sent as an array by the script versions, so you can segment from day one instead of asking later.
A hidden source field records where the form sits — footer, blog post, pricing page — so you can see which placement actually brings subscribers.
Fields in this template
| Field | Name | Type | Required | Why it is there |
|---|---|---|---|---|
email | Yes | Required, first, and the only thing most people will type. | ||
| First name | first_name | Short text | No | Optional — for a personal greeting in your email tool. |
| What would you like to hear about? | topics | Multiple choice | No | Optional multiple choice, sent as an array for segmenting. |
| Email me the newsletter. I can unsubscribe at any time. | consent | Checkbox | Yes | Required and unticked by default: a recorded, deliberate opt-in. |
| (hidden) | source | Hidden value | No | Hidden. Change it per placement to compare where signups come from. |
Each name becomes a column in your dashboard and a row in the notification email. The hidden _honey field is the honeypot; _subject sets the email's subject line. Both are reserved fields and never appear in the field table.
Make it yours
- For a one-line footer form, keep only the email field, the consent checkbox and the button.
- Change
sourceper page, or fill it withlocation.pathnamefrom JavaScript. - Send people to a confirmation page with a custom redirect that says what to expect and how often.
- If you run double opt-in, let your email tool send the confirm-your-address message. Auto Reply sends a single acknowledgement, not a confirmation link, so it cannot complete a double opt-in on its own.
Where the submissions go
- Every signup is stored in your dashboard and can be exported to CSV and imported into any mailing tool, on every plan.
- On Starter and above, add subscribers to your email tool as they arrive with a webhook, Zapier or n8n.
- Submissions flagged as spam stay in the dashboard's spam tab and are never forwarded, so bot signups do not reach your mailing tool.
Newsletter Signups: plans, destinations and a worked setup →
Before you publish it
- Create a form in the Formboost dashboard and replace
YOUR_ENDPOINT_IDwith its ID. - Pick one variant: HTML posts and reloads, HTML + JS and React send JSON with
fetch()and stay on the page. - For the plain HTML version, add a hidden
_redirectwith anhttps://thank-you page on the same site as the form — anything else falls back to Formboost's page. - Submit it once from the live page and check the submission in your dashboard. Every request is acknowledged, so the dashboard is the real test. Troubleshooting covers the rest.
More form templates
Survey form template
Four question types — a 0–10 score, single choice, multiple choice and an open answer — in one page.
Feedback form template
A rating, two open questions and the page it came from — short enough that people finish it.
Event registration form template
Attendee details, how they will attend, and the two questions organisers always forget to ask.
All form templates
Every template, in HTML, React and Tailwind.
Put the newsletter signup form live
Free forever: 500 submissions a month across 10 forms, no card.