Contact Form
A fully customizable contact/email form with animated micro-interactions, validation, and backend integration with Nodemailer.
Installation
Backend Setup
The form expects a backend endpoint at /api/contact that accepts a POST request with JSON:
Example Next.js Route Handler
updated your /api/contact/route.ts to actually send emails using Nodemailer.
- Install Nodemailer
- Create environment variables
Add these to .env(or .env.local):
1EMAIL_HOST=smtp.example.com2EMAIL_PORT=5873EMAIL_USER=your_email@example.com4EMAIL_PASS=your_email_password5EMAIL_TO=recipient@example.com
EMAIL_TO is where contact form messages will be sent.
For Gmail, you can use smtp.gmail.com with an app password.
- Backend API: /app/api/contact/route.ts
1import { NextResponse } from "next/server";2import nodemailer from "nodemailer";3 4export async function POST(request: Request) {5 try {6 const { name, email, subject, message } = await request.json();7 8 if (!name || !email || !subject || !message) {9 return NextResponse.json(10 { error: "All fields are required" },11 { status: 400 },12 );13 }14 15 if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {16 return NextResponse.json(17 { error: "Invalid email address" },18 { status: 400 },19 );20 }21 22 // Create transporter23 const transporter = nodemailer.createTransport({24 host: process.env.EMAIL_HOST,25 port: Number(process.env.EMAIL_PORT),26 secure: Number(process.env.EMAIL_PORT) === 465, // true for 465, false for other ports27 auth: {28 user: process.env.EMAIL_USER,29 pass: process.env.EMAIL_PASS,30 },31 });32 33 // Send email34 await transporter.sendMail({35 from: `"${name}" <${email}>`,36 to: process.env.EMAIL_TO,37 subject: `[Contact Form] ${subject}`,38 text: message,39 html: `<p>${message}</p><p>From: ${name} (${email})</p>`,40 });41 42 return NextResponse.json({43 success: true,44 message: "Message sent successfully!",45 });46 } catch (error) {47 console.error("Contact form error:", error);48 return NextResponse.json({ error: "Server error" }, { status: 500 });49 }50}
- Frontend: Page.tsx
No changes are needed beyond what you already have. Just make sure the handleSubmit calls your API:
1const handleSubmit = async (data: Record<string, string>) => {2 const response = await fetch("/api/contact", {3 method: "POST",4 headers: { "Content-Type": "application/json" },5 body: JSON.stringify(data),6 });7 return response.json();8};
Props
Usage
1"use client";2 3import {4 ContactForm,5 ContactField,6 ContactTextArea,7 ContactSubmit,8} from "@/components/ui/contact-form";9 10export default function Page() {11 const handleSubmit = async (data: Record<string, string>) => {12 const response = await fetch("/api/contact", {13 method: "POST",14 headers: { "Content-Type": "application/json" },15 body: JSON.stringify(data),16 });17 return response.json();18 };19 20 return (21 <div className="flex min-h-screen items-center justify-center bg-neutral-100 p-4 dark:bg-neutral-900">22 <ContactForm onSubmit={handleSubmit} className="w-full max-w-md">23 <ContactField name="name" label="Name" />24 <ContactField name="email" label="Email" type="email" />25 <ContactField name="subject" label="Subject" />26 <ContactTextArea name="message" label="Message" />27 <ContactSubmit>Send Message</ContactSubmit>28 </ContactForm>29 </div>30 );31}
Features
- Fully client-compatible for Next.js 13+
- Framer Motion spring animations with float labels
- Micro-interactions for input focus, hover, tap, and submit
- Validation & error handling
- Backend-ready API endpoint
- Light/Dark mode support
- Fully customizable via props, className, and onSubmit handler