Real Project: Deploy A Contact Form Site
The goal is not to copy a production-ready email integration into every app. The goal is to understand the shape of a safe deployment. A contact form is simple enough for beginners, but it includes real concerns: spam, validation, secrets, provider failure, privacy, logs, and client handoff.
Project Shape
The site has two parts. The frontend is a static page with a form. The backend is a Vercel Function or framework API route that receives the form submission and calls an email, notification, or form-processing provider. The browser never sees the provider secret.
The request flow is:
Visitor opens the site.
Visitor fills out the contact form.
Browser sends a POST request to /api/contact.
Server-side function validates the submission.
Function calls the email or form provider with a private token.
Function returns a small success or error response.
Runtime Logs record safe operational details.
This project can be built with plain HTML and a small Function, a Vite frontend plus an api/ directory, or a Next.js app with a Route Handler. Use the structure that matches the project, but keep the security boundary the same.
Frontend Responsibilities
The frontend should collect only the fields you need. A beginner contact form might ask for name, email, subject, and message. Avoid collecting phone numbers, company size, budget, or personal details unless the project actually needs them.
Frontend validation improves user experience. It can show required fields, prevent obviously empty submissions, limit message length, and display loading and success states. But frontend validation is not security. The server endpoint must validate again because anyone can bypass browser code.
The frontend should not contain provider credentials. Do not put email provider tokens, SMTP passwords, private API keys, or webhook secrets in JavaScript, HTML, public environment variables, or hidden form fields. Hidden form fields are visible to users and can be changed.
Server Endpoint Responsibilities
The server endpoint owns trust. It receives untrusted input, validates it, reads the provider secret from Environment Variables, calls the provider, and returns a safe response.
A conceptual endpoint should handle:
Allowed HTTP method.
Required fields.
Maximum message length.
Basic email shape.
Spam or abuse checks where appropriate.
Missing provider configuration.
Provider failure.
Safe response messages.
Safe logging.
Return useful status codes. Invalid user input should not look like a server crash. Provider downtime should not expose raw provider details. The browser should receive enough information to guide the user, while Runtime Logs keep developer details.
Environment Variable Setup
Store the provider secret in Vercel Environment Variables. Use separate values for Local, Preview, and Production when the provider supports it. Preview should usually send to a test inbox or sandbox provider account. Production should send to the real destination only after review.
A typical variable name might be:
CONTACT_PROVIDER_API_KEY
CONTACT_TO_EMAIL
Do not prefix private variables with browser-exposure prefixes such as VITE_ or NEXT_PUBLIC_. Do not commit .env files with real values. Do not paste secrets into screenshots, issue comments, or logs.
After adding or changing Environment Variables, redeploy the project when needed. If the function reads the value at runtime, still create a fresh Preview Deployment for clarity before promoting the change.
Provider Choice
The provider could be an email API, form backend, help desk, CRM, chat tool, or workflow service. Choose a provider that matches the project. A personal portfolio may only need a simple email. A client site may need submissions routed to a CRM. A business app may need audit trails, consent handling, and support workflows.
Before using a provider, review its pricing, rate limits, spam controls, privacy terms, and account ownership. For client work, the provider account should usually belong to the client or their organization, not the developer's personal account.
If the provider has sandbox or test mode, use it in Preview. Do not send test messages to real customers or production lists.
Preview Deployment Review
Deploy the first version as a Preview Deployment. Test the static page and the function before connecting the production domain.
A useful preview review includes:
Homepage loads.
Form displays correctly on desktop and mobile.
Required fields are enforced in the browser.
Server rejects missing fields.
Server rejects oversized messages.
Valid test submission reaches the test inbox or provider.
Provider secret is not visible in browser JavaScript.
Runtime Logs show safe operational details.
Preview uses staging or test provider credentials.
Also test failure states. Temporarily remove or change the provider key in Preview, or use a provider test mode that simulates failure. The user should see a polite error, and the logs should show enough detail for a developer to debug.
Production Domain
After the preview works, add the production domain. Decide whether the canonical domain is the apex domain, the www domain, or both with a redirect. Configure DNS carefully and wait for verification. Vercel can provide HTTPS for the domain, but you should still test the final production URL.
After the domain is live, test:
https://example.com loads.
www or apex redirects as intended.
The contact form submits from the production domain.
Provider callbacks or allowed origins include the production domain if needed.
HTTPS is active.
Old temporary preview URLs are not being sent to users.
Do not announce the domain until the form has been tested from the real production URL. Provider allowlists, CORS rules, spam controls, and email routing sometimes behave differently by domain.
Logs And Privacy
Runtime Logs are essential for this project because the main behavior happens in a Function. Log safe events: request received, validation failed, provider unavailable, message accepted. Do not log full message bodies, API keys, cookies, authorization headers, or private contact details.
Contact form messages can contain personal information. Treat them respectfully. Decide where submissions are stored, who receives them, and how long they are kept. If the site belongs to a client, confirm privacy policy language and consent requirements before launch.
Abuse Controls
Public contact forms attract spam. A beginner project can start with simple controls: required fields, length limits, honeypot fields, rate controls where available, provider-side spam filtering, and clear logging. For higher-risk sites, use stronger anti-abuse tools and review accessibility before adding challenges.
Do not let the form become a free email-sending endpoint. The server should decide the destination email address. The browser should not be allowed to choose arbitrary recipients. If the form includes a subject, sanitize and constrain it before passing it to a provider.
Release Checklist
Before calling the project done, review the whole deployment:
The site is on the correct Vercel team and plan.
Commercial-use requirements are reviewed.
Provider account ownership is clear.
Preview uses test credentials.
Production uses production credentials.
No secrets are committed.
The function validates input server-side.
The production domain and HTTPS work.
Runtime Logs are clean after smoke testing.
Usage and provider costs are understood.
This checklist is more important than the specific provider code. Providers change, but the deployment habits stay useful.
Ownership And Handoff
A contact form looks small, but it often crosses several ownership boundaries. The website may be built by a developer, hosted in a Vercel team, connected to an email provider, received by a client inbox, and mentioned in a privacy policy. If any of those pieces belong to the wrong account, future maintenance becomes harder.
For a client project, confirm who owns the domain, Vercel team, provider account, destination inbox, and billing. The client should be able to rotate the provider key, change the receiving address, and remove developer access when the project ends. A form that depends on a developer's personal email account is not a clean handoff.
Document the operational path in a short note. Name the endpoint, the Environment Variables it needs, the provider account, the destination inbox, the expected success behavior, and where to check logs. That note helps the next developer fix the form without reverse-engineering the deployment under pressure.
Testing The Unhappy Paths
A contact form is only trustworthy if the failure states are designed. Users will mistype email addresses, submit empty messages, lose network access, double-click the submit button, or send messages while the provider is unavailable. The project should respond clearly instead of silently failing.
Test the button while a request is already pending. Test a message that is too long. Test a missing provider key in Preview. Test a provider rejection if the provider offers a sandbox mode. Test a browser refresh after submission. These checks reveal whether the UI is honest and whether the server endpoint returns useful status codes.
For commercial projects, also test who receives the message and how quickly they can respond. A form that technically sends email but lands in spam or an unmonitored inbox is still broken from the user's perspective.
Accessibility And Content
The form should be usable by keyboard and screen-reader users. Labels should be connected to inputs. Error messages should explain what needs to be fixed. The submit button should show a loading state while the request is in progress. Success text should tell the user what happens next.
Content matters too. If the business promises a response within one business day, the team must be ready to honor that promise. If the form collects personal information, the surrounding page should make the purpose clear. Deployment quality includes what the user is told, not only whether the HTTP request succeeds.
Common Mistakes
The first mistake is putting the email API key in frontend JavaScript.
The second mistake is trusting browser validation and skipping server validation.
The third mistake is testing only the happy path.
The fourth mistake is sending Preview submissions to a real production inbox without warning.
The fifth mistake is logging full contact messages during debugging.
The sixth mistake is launching the custom domain before testing the form on that domain.
Review Questions
- Why does a contact form need a server-side endpoint?
- What should the frontend validate, and what must the server validate?
- Where should the provider secret live?
- Why should Preview use test credentials when possible?
- What should Runtime Logs include and avoid?
- What should be checked after adding the production domain?