Skip to content

Instantly share code, notes, and snippets.

@gn-bcampbell
Created July 15, 2024 21:33
Show Gist options
  • Save gn-bcampbell/2e6ca985aa6c53fa11f6ca5dd5160abe to your computer and use it in GitHub Desktop.
Save gn-bcampbell/2e6ca985aa6c53fa11f6ca5dd5160abe to your computer and use it in GitHub Desktop.
Using NextJS, Zod, TypeScript, ShadCN and EmailJS to create a contact form
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import emailjs from "@emailjs/browser";
import React, { useRef } from "react";
import { env } from "~/env";
import { Button } from "~/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "~/components/ui/form";
import { Input } from "~/components/ui/input";
import { useToast } from "~/components/ui/use-toast";
import { Textarea } from "~/components/ui/textarea";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPaperPlane } from "@fortawesome/free-solid-svg-icons"; //optional
const FormSchema = z.object({
username: z.string().min(2, {
message: "Username must be at least 2 characters.",
}),
email: z.string().email("Invalid email address.").min(2, {
message: "Email must be at least 2 characters.",
}),
message: z.string().min(10, {
message: "Message should be at least 10 characters.",
}),
});
export const Contact = () => {
// pass into form to support EmailJS requirements
const formRef = useRef<HTMLFormElement | null>(null);
const { toast } = useToast();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
username: "",
email: "",
message: "",
},
});
const onSubmit = (data: z.infer<typeof FormSchema>) => {
console.log("clicked");
console.log(data);
if (formRef.current) {
emailjs
.sendForm(
env.NEXT_PUBLIC_EMAILJS_SERVICE_ID,
env.NEXT_PUBLIC_EMAILJS_TEMPLATE_ID,
formRef.current,
{
publicKey: env.NEXT_PUBLIC_EMAILJS_PUBLIC_KEY,
},
)
.then(
() => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
toast({
title: "Email sent.",
description: `Thanks ${data.username}, I'll be in touch.`,
});
form.reset(); //clear the fields after submission
},
(error) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
toast({
variant: "destructive",
title: "Email failed to send.",
description: `If this continues, please try using the link at the bottom of the page.`,
});
// console.warn("FAILED...", JSON.stringify(error));
},
);
}
};
return (
<>
<Form {...form}>
<form
ref={formRef}
onSubmit={form.handleSubmit(onSubmit)}
className="w-2/3 space-y-6"
>
<FormField
name="username"
render={({ field }) => (
<FormItem>
<FormLabel className="text-lg">Name</FormLabel>
<FormControl>
<Input
className="border-primary bg-white"
placeholder="Your Name"
{...field}
/>
</FormControl>
<FormMessage className="text-xs text-red-600" />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel className="text-lg">Email</FormLabel>
<FormControl>
<Input
className="border-primary bg-white"
placeholder="Email Address"
{...field}
/>
</FormControl>
<FormMessage className="text-xs text-red-600" />
</FormItem>
)}
/>
<FormField
control={form.control}
name="message"
render={({ field }) => (
<FormItem>
<FormLabel className="text-lg">Message</FormLabel>
<FormControl>
<Textarea
className="border-primary bg-white"
placeholder="Type your message here."
id="message"
{...field}
/>
</FormControl>
<FormMessage className="text-xs text-red-600" />
</FormItem>
)}
/>
<Button
type="submit"
className="text-md text-white hover:bg-secondary"
>
Send{" "}
<FontAwesomeIcon
icon={faPaperPlane}
size="lg"
className="ml-2 p-2"
/>
</Button>
</form>
</Form>
</>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment