Skip to content

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>
);
};