Skip to main content

Party & Celebration RSVP Form

A vibrant, casual party and gathering RSVP form styled with Tailwind CSS in warm berry and sunset tones. Captures attendance responses, optional plus-one guest names, dietary requirements (vegan, gluten-free, allergies), and dance song requests, piping guest list updates straight into your Google Sheet or Telegram.

Attendance status selector (Attending vs Can't make it)
Optional plus-one (+1) guest field toggle
Dietary restrictions and preferences tags
Song request / host note textarea
Instant Telegram, Discord & Google Sheets sync
Live Form PreviewComponent Sandbox

Summer Rooftop Gathering & Dinner

Saturday, July 25th • 7:00 PM • Sunset Terrace

Pre-configured for Backforms endpoint with guest list sync
Honeypot anti-spam active
Zero backend needed
style="color:#9ece6a;">"use client";

import { useState } from style="color:#9ece6a;">"react";
import { PartyPopper, Heart, Music, UtensilsCrossed, UserPlus } from style="color:#9ece6a;">"lucide-react";

export default function PartyRsvpForm() {
  const [guestName, setGuestName] = useState(style="color:#9ece6a;">"");
  const [email, setEmail] = useState(style="color:#9ece6a;">"");
  const [attendance, setAttendance] = useState<style="color:#9ece6a;">"attending" | style="color:#9ece6a;">"declined">(style="color:#9ece6a;">"attending");
  const [hasPlusOne, setHasPlusOne] = useState(false);
  const [plusOneName, setPlusOneName] = useState(style="color:#9ece6a;">"");
  const [dietary, setDietary] = useState(style="color:#9ece6a;">"None");
  const [songRequest, setSongRequest] = useState(style="color:#9ece6a;">"");
  const [status, setStatus] = useState<style="color:#9ece6a;">"idle" | style="color:#9ece6a;">"loading" | style="color:#9ece6a;">"success" | style="color:#9ece6a;">"error">(style="color:#9ece6a;">"idle");
  const [errorMessage, setErrorMessage] = useState(style="color:#9ece6a;">"");

  const dietaryOptions = [style="color:#9ece6a;">"None", style="color:#9ece6a;">"Vegetarian", style="color:#9ece6a;">"Vegan", style="color:#9ece6a;">"Gluten-Free"];

  async function handleSubmit(e: React.FormEvent<style="color:#f7768e;font-weight:600;">HTMLFormElement>) {
    e.preventDefault();
    setStatus(style="color:#9ece6a;">"loading");
    setErrorMessage(style="color:#9ece6a;">"");

    const form = e.currentTarget;
    const formData = new FormData(form);
    formData.set(style="color:#9ece6a;">"attendance", attendance);
    formData.set(style="color:#9ece6a;">"hasPlusOne", hasPlusOne ? style="color:#9ece6a;">"yes" : style="color:#9ece6a;">"no");
    if (hasPlusOne) formData.set(style="color:#9ece6a;">"plusOneName", plusOneName);
    formData.set(style="color:#9ece6a;">"dietary", dietary);

    try {
      style=style="color:#9ece6a;">"color:#565f89;font-style:italic;">// Replace YOUR_FORM_ID with your Backforms endpoint ID from app.backforms.com
      const response = await fetch(style="color:#9ece6a;">"https:style="color:#565f89;font-style:italic;style="color:#9ece6a;">">//backforms.com/f/YOUR_FORM_ID", {
        method: style="color:#9ece6a;">"POST",
        headers: {
          Accept: style="color:#9ece6a;">"application/json",
        },
        body: formData,
      });

      if (response.ok) {
        setStatus(style="color:#9ece6a;">"success");
      } else {
        const data = await response.json().catch(() => null);
        setErrorMessage(data?.error || style="color:#9ece6a;">"RSVP submission failed. Please try again.");
        setStatus(style="color:#9ece6a;">"error");
      }
    } catch {
      setErrorMessage(style="color:#9ece6a;">"Network error. Please check your internet connection.");
      setStatus(style="color:#9ece6a;">"error");
    }
  }

  return (
    <style="color:#f7768e;font-weight:600;">div className=style="color:#9ece6a;">"flex flex-col items-center justify-center min-h-[500px] p-4">
      <style="color:#f7768e;font-weight:600;">div className=style="color:#9ece6a;">"w-full max-w-md bg-gradient-to-b from-[#1b1018] to-[#0e070c] border border-pink-500/30 rounded-2xl p-6 sm:p-7 shadow-2xl text-zinc-100">
        <style="color:#f7768e;font-weight:600;">h2 className=style="color:#9ece6a;">"text-xl sm:text-2xl font-extrabold text-white mb-1">
          Alex&apos;s Birthday Bash &amp; Rooftop BBQ
        </style="color:#f7768e;font-weight:600;">h2>
        <style="color:#f7768e;font-weight:600;">p className=style="color:#9ece6a;">"text-xs text-zinc-400 mb-5 leading-relaxed">
          Saturday, July 25th • 7:00 PM • Sunset Terrace (BYOB &amp; good vibes)
        </style="color:#f7768e;font-weight:600;">p>

        {status === style="color:#9ece6a;">"success" ? (
          <style="color:#f7768e;font-weight:600;">div className=style="color:#9ece6a;">"p-5 bg-pink-950/60 border border-emerald-500/40 text-emerald-300 rounded-xl text-center space-y-2">
            <style="color:#f7768e;font-weight:600;">div className=style="color:#9ece6a;">"size-10 mx-auto rounded-full bg-emerald-950/70 border border-emerald-500/40 flex items-center justify-center text-emerald-400">
              <style="color:#f7768e;font-weight:600;">PartyPopper className=style="color:#9ece6a;">"size-5" />
            </style="color:#f7768e;font-weight:600;">div>
            <style="color:#f7768e;font-weight:600;">p className=style="color:#9ece6a;">"font-extrabold text-lg text-white">
              {attendance === style="color:#9ece6a;">"attending" ? style="color:#9ece6a;">"You're on the Guest List! 🎉" : style="color:#9ece6a;">"We'll Miss You! ❤️"}
            </style="color:#f7768e;font-weight:600;">p>
            <style="color:#f7768e;font-weight:600;">p className=style="color:#9ece6a;">"text-xs text-zinc-300">
              Thank you, {guestName}! The host has been notified of your response.
            </style="color:#f7768e;font-weight:600;">p>
            <style="color:#f7768e;font-weight:600;">button
              type=style="color:#9ece6a;">"button"
              onClick={() => setStatus(style="color:#9ece6a;">"idle")}
              className=style="color:#9ece6a;">"mt-3 px-4 py-1.5 bg-pink-600 hover:bg-pink-500 text-zinc-950 font-bold rounded-lg text-xs transition cursor-pointer"
            >
              Update RSVP
            </style="color:#f7768e;font-weight:600;">button>
          </style="color:#f7768e;font-weight:600;">div>
        ) : (
          <style="color:#f7768e;font-weight:600;">form onSubmit={handleSubmit} className=style="color:#9ece6a;">"space-y-4">
            {style=style="color:#9ece6a;">"color:#565f89;font-style:italic;">/* Invisible Honeypot Spam Trap */}
            <style="color:#f7768e;font-weight:600;">input
              type=style="color:#9ece6a;">"text"
              name=style="color:#9ece6a;">"_gotcha"
              style={{ display: style="color:#9ece6a;">"none" }}
              tabIndex={-1}
              autoComplete=style="color:#9ece6a;">"off"
            />

            {status === style="color:#9ece6a;">"error" && (
              <style="color:#f7768e;font-weight:600;">div className=style="color:#9ece6a;">"p-3 bg-red-950/80 border border-red-500/50 text-red-300 text-xs rounded-lg">
                {errorMessage}
              </style="color:#f7768e;font-weight:600;">div>
            )}

            {style=style="color:#9ece6a;">"color:#565f89;font-style:italic;">/* Attendance Toggle */}
            <style="color:#f7768e;font-weight:600;">div className=style="color:#9ece6a;">"grid grid-cols-2 gap-2">
              <style="color:#f7768e;font-weight:600;">button
                type=style="color:#9ece6a;">"button"
                onClick={() => setAttendance(style="color:#9ece6a;">"attending")}
                className={`py-2.5 px-3 rounded-xl text-xs font-bold transition flex items-center justify-center gap-1.5 cursor-pointer ${
                  attendance === style="color:#9ece6a;">"attending"
                    ? style="color:#9ece6a;">"bg-gradient-to-r from-pink-500 to-rose-500 text-zinc-950 shadow-md"
                    : style="color:#9ece6a;">"bg-[#251520] text-zinc-400 border border-pink-500/20 hover:text-white"
                }`}
              >
                <style="color:#f7768e;font-weight:600;">PartyPopper className=style="color:#9ece6a;">"size-4" />
                <style="color:#f7768e;font-weight:600;">span>Count Me In!</style="color:#f7768e;font-weight:600;">span>
              </style="color:#f7768e;font-weight:600;">button>

              <style="color:#f7768e;font-weight:600;">button
                type=style="color:#9ece6a;">"button"
                onClick={() => setAttendance(style="color:#9ece6a;">"declined")}
                className={`py-2.5 px-3 rounded-xl text-xs font-bold transition flex items-center justify-center gap-1.5 cursor-pointer ${
                  attendance === style="color:#9ece6a;">"declined"
                    ? style="color:#9ece6a;">"bg-zinc-800 text-rose-300 border border-rose-500/50"
                    : style="color:#9ece6a;">"bg-[#251520] text-zinc-400 border border-pink-500/20 hover:text-white"
                }`}
              >
                <style="color:#f7768e;font-weight:600;">Heart className=style="color:#9ece6a;">"size-4" />
                <style="color:#f7768e;font-weight:600;">span>Can&apos;t Make It</style="color:#f7768e;font-weight:600;">span>
              </style="color:#f7768e;font-weight:600;">button>
            </style="color:#f7768e;font-weight:600;">div>

            <style="color:#f7768e;font-weight:600;">div className=style="color:#9ece6a;">"grid grid-cols-1 sm:grid-cols-2 gap-2.5">
              <style="color:#f7768e;font-weight:600;">div>
                <style="color:#f7768e;font-weight:600;">label className=style="color:#9ece6a;">"block text-xs font-medium text-zinc-300 mb-1" htmlFor=style="color:#9ece6a;">"guestName">
                  Your Name
                </style="color:#f7768e;font-weight:600;">label>
                <style="color:#f7768e;font-weight:600;">input
                  id=style="color:#9ece6a;">"guestName"
                  name=style="color:#9ece6a;">"guestName"
                  type=style="color:#9ece6a;">"text"
                  required
                  placeholder=style="color:#9ece6a;">"Morgan Guest"
                  value={guestName}
                  onChange={(e) => setGuestName(e.target.value)}
                  className=style="color:#9ece6a;">"w-full bg-[#251520] text-white border border-pink-500/25 rounded-lg px-3 py-1.5 text-xs placeholder:text-zinc-500 focus:outline-none focus:border-pink-400 transition"
                />
              </style="color:#f7768e;font-weight:600;">div>

              <style="color:#f7768e;font-weight:600;">div>
                <style="color:#f7768e;font-weight:600;">label className=style="color:#9ece6a;">"block text-xs font-medium text-zinc-300 mb-1" htmlFor=style="color:#9ece6a;">"email">
                  Email or Phone
                </style="color:#f7768e;font-weight:600;">label>
                <style="color:#f7768e;font-weight:600;">input
                  id=style="color:#9ece6a;">"email"
                  name=style="color:#9ece6a;">"email"
                  type=style="color:#9ece6a;">"text"
                  required
                  placeholder=style="color:#9ece6a;">"morgan@party.com"
                  value={email}
                  onChange={(e) => setEmail(e.target.value)}
                  className=style="color:#9ece6a;">"w-full bg-[#251520] text-white border border-pink-500/25 rounded-lg px-3 py-1.5 text-xs placeholder:text-zinc-500 focus:outline-none focus:border-pink-400 transition"
                />
              </style="color:#f7768e;font-weight:600;">div>
            </style="color:#f7768e;font-weight:600;">div>

            {attendance === style="color:#9ece6a;">"attending" && (
              <style="color:#f7768e;font-weight:600;">div className=style="color:#9ece6a;">"p-3 bg-[#251520]/80 border border-pink-500/20 rounded-xl space-y-2">
                <style="color:#f7768e;font-weight:600;">label className=style="color:#9ece6a;">"flex items-center justify-between cursor-pointer">
                  <style="color:#f7768e;font-weight:600;">span className=style="color:#9ece6a;">"flex items-center gap-2 text-xs font-medium text-zinc-200">
                    <style="color:#f7768e;font-weight:600;">UserPlus className=style="color:#9ece6a;">"size-3.5 text-pink-400" />
                    <style="color:#f7768e;font-weight:600;">span>Bringing a Plus-One (+1)?</style="color:#f7768e;font-weight:600;">span>
                  </style="color:#f7768e;font-weight:600;">span>
                  <style="color:#f7768e;font-weight:600;">input
                    type=style="color:#9ece6a;">"checkbox"
                    checked={hasPlusOne}
                    onChange={(e) => setHasPlusOne(e.target.checked)}
                    className=style="color:#9ece6a;">"size-4 accent-pink-500 rounded cursor-pointer"
                  />
                </style="color:#f7768e;font-weight:600;">label>

                {hasPlusOne && (
                  <style="color:#f7768e;font-weight:600;">input
                    type=style="color:#9ece6a;">"text"
                    name=style="color:#9ece6a;">"plusOneName"
                    placeholder=style="color:#9ece6a;">"Plus-one guest full name"
                    value={plusOneName}
                    onChange={(e) => setPlusOneName(e.target.value)}
                    className=style="color:#9ece6a;">"w-full bg-[#1b0e17] text-white border border-pink-500/30 rounded-lg px-3 py-1.5 text-xs placeholder:text-zinc-500 focus:outline-none focus:border-pink-400 transition"
                  />
                )}
              </style="color:#f7768e;font-weight:600;">div>
            )}

            {attendance === style="color:#9ece6a;">"attending" && (
              <style="color:#f7768e;font-weight:600;">div>
                <style="color:#f7768e;font-weight:600;">label className=style="color:#9ece6a;">"block text-xs font-medium text-zinc-300 mb-1.5 flex items-center gap-1.5">
                  <style="color:#f7768e;font-weight:600;">UtensilsCrossed className=style="color:#9ece6a;">"size-3.5 text-amber-400" />
                  <style="color:#f7768e;font-weight:600;">span>Dietary Requirements</style="color:#f7768e;font-weight:600;">span>
                </style="color:#f7768e;font-weight:600;">label>
                <style="color:#f7768e;font-weight:600;">div className=style="color:#9ece6a;">"grid grid-cols-4 gap-1.5">
                  {dietaryOptions.map((opt) => (
                    <style="color:#f7768e;font-weight:600;">button
                      key={opt}
                      type=style="color:#9ece6a;">"button"
                      onClick={() => setDietary(opt)}
                      className={`py-1.5 px-1 text-[11px] font-mono rounded-lg transition-all text-center cursor-pointer truncate ${
                        dietary === opt
                          ? style="color:#9ece6a;">"bg-amber-400 text-zinc-950 font-bold shadow-xs"
                          : style="color:#9ece6a;">"bg-[#251520] text-zinc-400 border border-pink-500/20 hover:text-white"
                      }`}
                    >
                      {opt}
                    </style="color:#f7768e;font-weight:600;">button>
                  ))}
                </style="color:#f7768e;font-weight:600;">div>
              </style="color:#f7768e;font-weight:600;">div>
            )}

            <style="color:#f7768e;font-weight:600;">div>
              <style="color:#f7768e;font-weight:600;">label htmlFor=style="color:#9ece6a;">"songRequest" className=style="color:#9ece6a;">"block text-xs font-medium text-zinc-300 mb-1 flex items-center gap-1.5">
                <style="color:#f7768e;font-weight:600;">Music className=style="color:#9ece6a;">"size-3.5 text-pink-400" />
                <style="color:#f7768e;font-weight:600;">span>Song Request or Note for Host</style="color:#f7768e;font-weight:600;">span>
              </style="color:#f7768e;font-weight:600;">label>
              <style="color:#f7768e;font-weight:600;">textarea
                id=style="color:#9ece6a;">"songRequest"
                name=style="color:#9ece6a;">"songRequest"
                rows={2}
                placeholder=style="color:#9ece6a;">"What song gets you dancing? Note for Alex?"
                value={songRequest}
                onChange={(e) => setSongRequest(e.target.value)}
                className=style="color:#9ece6a;">"w-full bg-[#251520] text-white border border-pink-500/25 rounded-lg px-3 py-1.5 text-xs placeholder:text-zinc-500 focus:outline-none focus:border-pink-400 transition resize-none"
              />
            </style="color:#f7768e;font-weight:600;">div>

            <style="color:#f7768e;font-weight:600;">button
              type=style="color:#9ece6a;">"submit"
              disabled={status === style="color:#9ece6a;">"loading"}
              className=style="color:#9ece6a;">"w-full mt-2 bg-gradient-to-r from-pink-500 via-rose-500 to-amber-400 hover:from-pink-400 hover:to-amber-300 text-zinc-950 font-extrabold py-2.5 px-4 rounded-xl transition cursor-pointer shadow-lg shadow-pink-500/25 disabled:opacity-60 flex items-center justify-center gap-2"
            >
              <style="color:#f7768e;font-weight:600;">PartyPopper className=style="color:#9ece6a;">"size-4 fill-zinc-950" />
              <style="color:#f7768e;font-weight:600;">span>{status === style="color:#9ece6a;">"loading" ? style="color:#9ece6a;">"Confirming RSVP..." : style="color:#9ece6a;">"Send Party RSVP 🎉"}</style="color:#f7768e;font-weight:600;">span>
            </style="color:#f7768e;font-weight:600;">button>
          </style="color:#f7768e;font-weight:600;">form>
        )}
      </style="color:#f7768e;font-weight:600;">div>
    </style="color:#f7768e;font-weight:600;">div>
  );
}
Replace YOUR_FORM_ID with your Backforms key

Connect this component to your Backforms endpoint

Create a free form endpoint in under 30 seconds. Zero backend servers or databases required.

Get Free Endpoint Key

Frequently Asked Questions & Technical Integration

Can Backforms notify me via Telegram when guests RSVP?

Yes! You can link a Telegram bot or Slack channel in your Backforms settings to receive instant ping notifications whenever a guest responds or updates their plus-one details.

How are dietary requirements and guest counts stored?

All fields including dietary choices (vegan, gluten-free, allergies) and plus-one names are captured in structured JSON and automatically synced to your party Google Sheet.

Does this template work for weddings, baby showers, or casual dinners?

Yes! The fields, labels, and color theme can be easily tailored to any social gathering, birthday, wedding, or team dinner.