Astro Action - Contact Form
Index.tx file in Actions
import { defineAction, z } from "astro:actions";import * as postmark from "postmark";
export const server = { contactForm: defineAction({ accept: "form", input: z.object({ name: z.string().min(1, "Name is required"), company: z.string().min(1, "Company is required"), email: z.string().email("Invalid email address"), phone: z.number().min(1, "Phone is required"), need: z.enum( [ "Marketing and Search Engine Optimization", "Website Design/Development", "Application Development", "Other", ], { errorMap: () => ({ message: "Please select a valid service" }), }, ), message: z .string() .min(1, "Project details are required") .max(1000, "Project details must be 1000 characters or less"), }), handler: async ({ name, company, email, phone, need, message }) => { const client = new postmark.ServerClient(import.meta.env.POSTMARK_TOKEN); try { const emailSubject = `Website Submission at brem.io`; let emailBody = ` <h1>Thank you for your interest in Brem!</h1> <br/> <p>We have received your quote request and will be in touch shortly.</p> <br/> <h3>Details:</h3> <p>Name: ${name}</p> <p>Name: ${company}</p> <p>Name: ${email}</p> <p>Name: ${phone}</p> <p>Name: ${need}</p> <p>Name: ${message}</p> `;
await client.sendEmail({ From: "Brem <info@brem.io>", To: email, Cc: `info@brem.io`, Subject: emailSubject, HtmlBody: emailBody, MessageStream: "outbound", }); } catch (error) { console.error("Error sending email via Postmark:", error); return new Response( JSON.stringify({ message: "Error sending email" }), { status: 500, }, ); } return { status: 200 }; }, }),};Contact Form Component
import { useState } from "react";import { InputField } from "@components/forms/Input";import { SelectInput } from "@components/forms/Select";import { TextArea } from "@components/forms/TextArea";import { Button } from "@components/forms/Button";import { actions } from "astro:actions";
export const ContactForm: React.FC = () => { const [isLoading, setIsLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); setIsLoading(true); try { const formData = new FormData(e.currentTarget); const result = await actions.contactForm(formData); if (result.success) { window.location.href = "/thank-you"; } else { console.error(result.error); setIsLoading(false); } } catch (error) { console.error(error); } }; return ( <form className="space-y-8" method="POST" onSubmit={handleSubmit}> <InputField htmlFor="name" label="Name" name="name" /> <InputField htmlFor="company" label="Company" name="company" /> <InputField htmlFor="email" label="Email" name="email" /> <InputField htmlFor="phone" label="Phone" name="phone" /> <SelectInput htmlFor="need" label="What service are you interested in?" name="need" defaultText="Select a service" options={[ "Marketing and Search Engine Optimization", "Website Design/Development", "Application Development", ]} /> <TextArea htmlFor="message" label="Message" name="message" /> <Button type="submit" buttonState={isLoading}> {isLoading ? "Preparing to send" : "Send Message"} </Button> </form> );};