1Create a FormSMTP project
Sign up free and add a project. Pick your email provider so the SMTP host and port fill in, enter your login, and click Test connection. Not sure which provider? Your existing mailbox works: see the Gmail or Zoho guides.
2Lock the project to your site
In Settings → Security, set Allowed websites to https://yoursite.com and Allowed recipients to your own address. Now the key in your page can only email you, only from your site, and only up to your hourly limit.
3Add the component
<!-- src/components/ContactForm.vue -->
<script setup>
import { ref } from "vue";
const endpoint = "https://app.formsmtp.com/api/v1/form/" + import.meta.env.VITE_FORMSMTP_KEY;
const status = ref("idle"); // idle | sending | sent | error
const error = ref("");
async function submit(e) {
status.value = "sending";
try {
const res = await fetch(endpoint, {
method: "POST",
headers: { Accept: "application/json" },
body: new URLSearchParams(new FormData(e.target)),
});
const data = await res.json();
if (!data.success) throw new Error(data.error);
status.value = "sent";
} catch (err) {
error.value = err.message;
status.value = "error";
}
}
</script>
<template>
<p v-if="status === 'sent'">Thanks! We'll be in touch soon.</p>
<form v-else @submit.prevent="submit">
<input name="name" placeholder="Name" required />
<input name="email" type="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required></textarea>
<input name="_gotcha" style="display: none" tabindex="-1" autocomplete="off" />
<button :disabled="status === 'sending'">
{{ status === "sending" ? "Sending…" : "Send" }}
</button>
<p v-if="status === 'error'" role="alert">{{ error }}</p>
</form>
</template>Put VITE_FORMSMTP_KEY=… in .env and use <ContactForm /> anywhere. Reading the fields with FormData means you can add inputs without touching the script.
4Using Nuxt?
Expose the key through public runtime config:
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
public: { formsmtpKey: "" }, // set NUXT_PUBLIC_FORMSMTP_KEY in .env
},
});Then swap the endpoint line in the component for:
const { public: { formsmtpKey } } = useRuntimeConfig();
const endpoint = "https://app.formsmtp.com/api/v1/form/" + formsmtpKey;This works with nuxi generate (fully static) as well as SSR, because the browser posts straight to FormSMTP.
Prefer v-model?
If your fields are already bound with v-model, send them as JSON instead of reading the form:
const fields = reactive({ name: "", email: "", message: "" });
const res = await fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify(fields),
});FAQ
Do I need a Nuxt server route?
No. The form posts directly to FormSMTP, so a static Nuxt site works. If you’d rather keep the key off the page entirely, call the send API from a Nuxt server route with a private runtime config value.
Does it work with Vue 2?
Yes. The fetch() call is the same; move the state into data() and the handler into methods.
Is it safe to put the API key in my front-end code?
Yes, with the project locked down. Form submissions always go to your own inbox, and you can restrict the key to your website and an hourly limit, so a copied key can’t be used to email anyone else.
Why did my message land in spam?
Usually the From address and the SMTP account don’t match, or the domain has no SPF/DKIM records. Send from the address you log in with, and check the Deliverability panel in FormSMTP’s Settings.