RSVP Form
Collect event RSVPs with attendance confirmation, plus-ones, and dietary notes. Works for weddings, conferences, and any gathering.
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.
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.
rsvp.html
<section class="ft-rsvp-demo" aria-label="RSVP form demo">
<div class="ft-window">
<div class="ft-window-bar" aria-hidden="true">
<span></span>
<span></span>
<span></span>
</div>
<form
id="rsvp-form"
class="ft-form"
action="https://formtorch.com/f/abc123"
method="POST"
accept-charset="UTF-8"
>
<div class="ft-form-heading">
<h2>RSVP Form</h2>
<p>Let us know if you'll be joining us.</p>
</div>
<input type="hidden" name="_formName" value="Event RSVP" />
<div class="ft-row">
<div class="ft-field">
<label for="ft-name">Full name</label>
<div class="ft-control">
<svg aria-hidden="true" viewBox="0 0 24 24">
<circle cx="12" cy="8" r="4" />
<path d="M4 20c0-4 3.6-7 8-7s8 3 8 7" />
</svg>
<input
id="ft-name"
type="text"
name="name"
placeholder="Your name"
autocomplete="name"
required
/>
</div>
</div>
<div class="ft-field">
<label for="ft-email">Email address</label>
<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="you@example.com"
autocomplete="email"
required
/>
</div>
</div>
</div>
<fieldset class="ft-field">
<legend>Will you be attending?</legend>
<div class="ft-attend-group">
<label class="ft-attend">
<input type="radio" name="attending" value="yes" required checked />
<span class="ft-attend-dot" aria-hidden="true"></span>
Yes, I'll be there
</label>
<label class="ft-attend">
<input type="radio" name="attending" value="no" />
<span class="ft-attend-dot" aria-hidden="true"></span>
No, I can't make it
</label>
<label class="ft-attend">
<input type="radio" name="attending" value="maybe" />
<span class="ft-attend-dot" aria-hidden="true"></span>
Maybe, not sure yet
</label>
</div>
</fieldset>
<div class="ft-field">
<label for="ft-guests">Number of guests</label>
<div class="ft-stepper">
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
<circle cx="9" cy="7" r="4" />
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
</svg>
<input
id="ft-guests"
class="ft-stepper-input"
type="number"
name="guests"
min="1"
max="10"
value="1"
inputmode="numeric"
/>
<div class="ft-stepper-controls">
<button
type="button"
class="ft-stepper-btn"
data-step="-1"
aria-label="Decrease number of guests"
>
−
</button>
<button
type="button"
class="ft-stepper-btn"
data-step="1"
aria-label="Increase number of guests"
>
+
</button>
</div>
</div>
</div>
<div class="ft-field">
<label for="ft-notes"> Additional notes <em>(optional)</em> </label>
<div class="ft-control ft-control--textarea">
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M12 20h9" />
<path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z" />
</svg>
<textarea
id="ft-notes"
name="notes"
rows="3"
placeholder="Any dietary restrictions or special requests?"
></textarea>
</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>Confirm RSVP</span>
</button>
<div id="ft-status" class="ft-status" hidden>
<svg aria-hidden="true" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" />
<path d="m9 12 2 2 4-4" />
</svg>
<div>
<p class="ft-status-title">Thanks! Your RSVP has been received.</p>
<p class="ft-status-sub">We look forward to seeing you there.</p>
</div>
</div>
</form>
</div>
</section>
<style>
html,
body {
margin: 0;
padding: 0;
background: #05070d;
}
.ft-rsvp-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-rsvp-demo,
.ft-rsvp-demo * {
box-sizing: border-box;
}
.ft-rsvp-demo::before,
.ft-rsvp-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-rsvp-demo::before {
top: 9%;
left: 5%;
}
.ft-rsvp-demo::after {
bottom: 9%;
right: 5%;
}
.ft-window {
position: relative;
z-index: 1;
width: min(100%, 600px);
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;
gap: 20px;
padding: clamp(32px, 6vw, 44px);
text-align: left;
}
.ft-form-heading h2 {
margin: 0;
color: #f8fafc;
font-size: clamp(26px, 5vw, 32px);
font-weight: 800;
line-height: 1.15;
letter-spacing: -0.03em;
}
.ft-form-heading p {
margin: 8px 0 0;
color: #94a3b8;
font-size: clamp(14px, 2.4vw, 15px);
line-height: 1.55;
}
.ft-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.ft-field {
width: 100%;
margin: 0;
padding: 0;
border: 0;
}
.ft-field label,
.ft-field legend {
display: block;
width: 100%;
margin: 0 0 10px;
padding: 0;
color: #e2e8f0;
font-size: 14px;
font-weight: 600;
}
.ft-field label em {
color: #64748b;
font-style: normal;
font-weight: 500;
}
.ft-control {
position: relative;
display: flex;
align-items: center;
}
.ft-control svg {
position: absolute;
left: 16px;
width: 18px;
height: 18px;
color: #3b82f6;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
pointer-events: none;
}
.ft-control input {
width: 100%;
height: 50px;
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: 15px;
padding: 0 16px 0 48px;
outline: none;
transition:
border-color 160ms ease,
background-color 160ms ease,
box-shadow 160ms ease;
}
.ft-control--textarea svg {
top: 16px;
}
.ft-control--textarea textarea {
width: 100%;
resize: vertical;
min-height: 96px;
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: 15px;
padding: 14px 16px 14px 48px;
outline: none;
transition:
border-color 160ms ease,
background-color 160ms ease,
box-shadow 160ms ease;
}
.ft-control input::placeholder,
.ft-control--textarea textarea::placeholder {
color: #64748b;
}
.ft-control input:hover,
.ft-control--textarea textarea:hover {
border-color: rgb(148 163 184 / 0.38);
}
.ft-control input:focus-visible,
.ft-control--textarea textarea:focus-visible {
border-color: #2563eb;
box-shadow: 0 0 0 4px rgb(37 99 235 / 0.18);
}
.ft-attend-group {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 10px;
}
.ft-attend {
position: relative;
display: flex;
align-items: center;
gap: 10px;
padding: 14px 16px;
border: 1px solid rgb(148 163 184 / 0.24);
border-radius: 10px;
background: rgb(15 23 42 / 0.5);
color: #e2e8f0;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition:
border-color 160ms ease,
background-color 160ms ease,
box-shadow 160ms ease;
}
.ft-attend:hover {
border-color: rgb(148 163 184 / 0.4);
}
.ft-attend input {
position: absolute;
inset: 0;
margin: 0;
opacity: 0;
cursor: pointer;
}
.ft-attend-dot {
position: relative;
flex: 0 0 auto;
width: 18px;
height: 18px;
border-radius: 999px;
border: 2px solid rgb(148 163 184 / 0.4);
transition: border-color 160ms ease;
}
.ft-attend-dot::after {
content: "";
position: absolute;
inset: 3px;
border-radius: 999px;
background: #3b82f6;
opacity: 0;
transition: opacity 160ms ease;
}
.ft-attend:has(input:checked) {
border-color: #2563eb;
background: rgb(37 99 235 / 0.12);
box-shadow:
0 0 0 1px rgb(37 99 235 / 0.35),
0 0 20px rgb(37 99 235 / 0.18);
}
.ft-attend:has(input:checked) .ft-attend-dot {
border-color: #3b82f6;
}
.ft-attend:has(input:checked) .ft-attend-dot::after {
opacity: 1;
}
.ft-attend input:focus-visible ~ .ft-attend-dot {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
.ft-stepper {
display: flex;
align-items: center;
gap: 12px;
height: 50px;
padding: 0 16px;
border: 1px solid rgb(148 163 184 / 0.24);
border-radius: 10px;
background: rgb(15 23 42 / 0.52);
}
.ft-stepper svg {
flex: 0 0 auto;
width: 20px;
height: 20px;
color: #3b82f6;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
.ft-stepper-input {
flex: 1 1 auto;
width: 100%;
border: 0;
background: transparent;
color: #f8fafc;
font: inherit;
font-size: 16px;
font-weight: 600;
outline: none;
appearance: textfield;
}
.ft-stepper-input::-webkit-inner-spin-button,
.ft-stepper-input::-webkit-outer-spin-button {
appearance: none;
margin: 0;
}
.ft-stepper-controls {
display: flex;
align-items: center;
gap: 6px;
}
.ft-stepper-btn {
appearance: none;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgb(148 163 184 / 0.24);
border-radius: 8px;
background: rgb(15 23 42 / 0.4);
color: #cbd5e1;
font-size: 16px;
line-height: 1;
cursor: pointer;
transition:
border-color 160ms ease,
background-color 160ms ease;
}
.ft-stepper-btn:hover:not(:disabled) {
border-color: rgb(148 163 184 / 0.4);
background: rgb(15 23 42 / 0.6);
}
.ft-stepper-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.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 {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 16px;
border: 1px solid rgb(52 211 153 / 0.3);
border-radius: 10px;
background: rgb(52 211 153 / 0.08);
}
.ft-status[hidden] {
display: none;
}
.ft-status svg {
flex: 0 0 auto;
margin-top: 2px;
width: 20px;
height: 20px;
color: #34d399;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
.ft-status-title {
margin: 0;
color: #f8fafc;
font-size: 14px;
font-weight: 600;
}
.ft-status-sub {
margin: 4px 0 0;
color: #94a3b8;
font-size: 13px;
}
.ft-status[data-state="error"] {
border-color: rgb(248 113 113 / 0.3);
background: rgb(248 113 113 / 0.08);
}
.ft-status[data-state="error"] svg {
color: #f87171;
}
@media (max-width: 480px) {
.ft-rsvp-demo {
padding: 18px;
}
.ft-rsvp-demo::before,
.ft-rsvp-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-row {
grid-template-columns: 1fr;
gap: 16px;
}
.ft-attend-group {
grid-template-columns: 1fr;
}
.ft-submit {
height: 52px;
}
}
</style>
<script>
const form = document.getElementById("rsvp-form");
const status = document.getElementById("ft-status");
const guestsInput = document.getElementById("ft-guests");
const button = form.querySelector("button[type='submit']");
const buttonLabel = button.querySelector("span");
const successHTML =
'<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><div><p class="ft-status-title">Thanks! Your RSVP has been received.</p><p class="ft-status-sub">We look forward to seeing you there.</p></div>';
const errorHTML =
'<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><div><p class="ft-status-title">Something went wrong.</p><p class="ft-status-sub">Please try again.</p></div>';
document.querySelectorAll("[data-step]").forEach((btn) => {
btn.addEventListener("click", () => {
const delta = Number(btn.dataset.step);
const min = Number(guestsInput.min) || 1;
const max = Number(guestsInput.max) || 10;
const next = Math.min(
Math.max(Number(guestsInput.value) + delta, min),
max
);
guestsInput.value = String(next);
});
});
form.addEventListener("submit", async (event) => {
event.preventDefault();
status.hidden = true;
status.removeAttribute("data-state");
button.disabled = true;
buttonLabel.textContent = "Sending...";
try {
const response = await fetch(form.action, {
method: "POST",
headers: {
"X-Requested-With": "XMLHttpRequest",
Accept: "application/json",
},
body: new FormData(form),
});
if (!response.ok) {
throw new Error("Submission failed");
}
status.innerHTML = successHTML;
status.hidden = false;
buttonLabel.textContent = "Confirm RSVP";
} catch {
status.dataset.state = "error";
status.innerHTML = errorHTML;
status.hidden = false;
buttonLabel.textContent = "Try again";
} finally {
button.disabled = false;
}
});
</script>
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 endpointPaste 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.