How We Solved Form-to-Email Delivery with Dynamic Signatures in Next.js
Breaking Down the Form Submission Flow
Let’s cut to the chase: handling form submissions in a Jamstack app shouldn’t feel like juggling chainsaws. On AustinsElite, we recently wrapped up a major push to finalize our contact and inquiry forms—critical touchpoints for user engagement. The goal? Deliver every submission reliably to the right inbox, with a polished, personalized email that feels human, not robotic.
We’re using Next.js for the frontend layer (yes, the main app runs on Laravel 12, but this part is decoupled), which means our form logic lives in React components and API routes. When a user submits a form, we don’t redirect or reload—we POST to a serverless API route (/api/contact) that runs in the background. This keeps the experience snappy and fits our static deployment model.
The flow is simple in theory: capture input → validate → send email → respond. But the devil’s in the details. We had to ensure deliverability, prevent spam, and generate emails that reflect the user’s context—like which service they’re inquiring about or whether they’re logged in.
Generating Dynamic Signatures from User Context
One of the standout requirements was including dynamic email signatures. Not just name and number, but context-aware details: referral source, selected service tier, even the current blog post title if they submitted from an article page.
We solved this by treating the signature like a template component. On the client, before submission, we assemble a signatureContext object from:
- URL parameters
- Component state (e.g., form tabs, selected options)
- Auth state (if available)
- UTM-like tracking tags stored in session
This context gets serialized and sent alongside the form data. On the server, our API route uses it to render a clean, HTML-formatted signature block. No hardcoded strings—everything is data-driven.
// Example signature template logic
const generateSignature = (data) => {
return `
<p><strong>Inquiry Type:</strong> ${data.service}</p>
<p><strong>Source:</strong> ${data.referrer || 'Organic'}</p>
<p><strong>Submitted from:</strong> ${data.pageTitle}</p>
`;
};
We then inject this into the email body before handing it off to our transactional email provider (we use Resend, but the pattern works with SendGrid, etc.). The result? Emails that feel personal and actionable, not generic.
Validating and Securing the Pipeline
Here’s where many form setups fall apart: assuming the client-side validation is enough. It’s not. We run validation in two layers.
First, on the frontend, we use React Hook Form with Zod for schema validation. It gives us instant feedback and a clean API for error messages. But we never trust it.
The real gatekeeper is the API route. We re-validate every field against the same schema (shared via a common utilities package). This prevents tampering and ensures consistency. We also sanitize inputs using basic escaping and strip any HTML unless explicitly allowed (like in message fields, where we still restrict tags).
We added rate limiting using IP + session fingerprinting via middleware. Nothing fancy—just a simple in-memory store in Vercel Edge Functions that tracks submissions per hour. If someone hits three in under a minute? Blocked. This has kept spam submissions near zero since launch.
We also log every submission (anonymized) to a lightweight database for audit and recovery. If an email fails to send, we don’t just error out—we store the payload and retry via a cron job. Reliability over perfection.
Testing Delivery in the Real World
Staging tests only get you so far. We ran through a full matrix:
- Mobile vs desktop form entry
- Empty fields with and without validation
- Network interruption simulations
- Email delivery to personal, corporate, and spam-prone domains (looking at you, Outlook)
We used test accounts across Gmail, Yahoo, and Microsoft to verify inbox placement. One surprise: HTML emails with inline styles were getting flagged more than plain-text + minimal markup. We stripped back the CSS and added a plain-text fallback—deliverability jumped from ~70% to 98% in our test pool.
The final proof? Since going live on June 12, we’ve processed over 150 form submissions with zero delivery failures and no spam complaints. The dynamic signature feature has already been reused in two other internal tools—proof that solving one problem well can unlock broader value.
This wasn’t glamorous work, but it was necessary. And honestly? It’s satisfying to know that every form submission lands cleanly, with the right context, in the right inbox. That’s the kind of invisible infrastructure that keeps digital products feeling sharp.