Skip to content

Contact Form

A fully customizable contact/email form with animated micro-interactions, validation, and backend integration with Nodemailer.

Installation

pnpm dlx shadcn@latest add https://ui.sanjid.shop/r/contact-form.json

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.

  1. Install Nodemailer
pnpm add nodemailer
  1. 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.

  1. 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}
  1. 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

PropTypeRequiredDefaultDescription
classNamestringNoOptional class name to append to the root element
onSubmit(data: Record<string,string>) => Promise<{ success: boolean; message?: string }>Nodefault mock submit functionCustom submit handler for backend integration

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

Build your next big idea with us

From lightning-fast landing pages to fully functional SaaS products, we turn your vision into reality. Book a call today and let's make something extraordinary.

They transformed our idea into a fully functional product in record time. Couldn't be happier!

Alex Chen

Founder, StartupX

The team's attention to detail and design expertise set our product apart in the market.

Sarah Kim

CTO, Innovate Inc