Send email with Vue
A form component that sends from the browser with a shippable public key — or a Nuxt server route when you have a backend.
Facts verified 2026-07-17 — corrections: hello@emailfast.dev
A Vue component can send email with no backend at all. Email Fast ships a browser SDK with EmailJS-compatible endpoints — the recipient always comes from the server-stored template, never from the request, so a public key in your frontend can't be abused to spam arbitrary addresses. Initialize with your public key, name a server-stored template, and hand sendForm the form element. If you run Nuxt or any backend, the second pattern keeps a secret key server-side instead.
npm install @email-fast/browser<!-- ContactForm.vue -->
<script setup>
import { ref } from "vue";
import emailfast from "@email-fast/browser";
emailfast.init({ publicKey: "ef_pub_..." }); // public by design — safe to ship
const state = ref("idle");
async function submit(e) {
state.value = "sending";
try {
await emailfast.sendForm("default", "contact_form", e.target);
state.value = "sent";
} catch {
state.value = "error";
}
}
</script>
<template>
<form @submit.prevent="submit">
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required />
<button :disabled="state === 'sending'">Send</button>
<p v-if="state === 'sent'">Thanks — we got it.</p>
<p v-if="state === 'error'">That didn't go through. Try again.</p>
</form>
</template>Or: send through your own backend
With Nuxt, one server route keeps the secret key out of the bundle entirely:
// server/api/contact.post.ts — Nuxt; the key stays on the server
import { EmailFast } from "@email-fast/nodejs";
const ef = new EmailFast({ apiKey: process.env.EMAILFAST_API_KEY!, baseUrl: "https://api.emailfast.dev" }); // ef_sandbox_… while testing
export default defineEventHandler(async (event) => {
const { message, formId } = await readBody(event);
const { id } = await ef.send({
to: "team@yourdomain.dev",
subject: "New contact message",
html: "<p>{{message}}</p>",
data: { message },
idempotency_key: `contact-${formId}`, // generated once per render — a double-submit replays
});
return { id };
});Gotchas
- Never reference a secret
ef_live_key in component code —VITE_…env vars are inlined into the client bundle by design. Onlyef_pub_may appear in the browser. - In Nuxt, keep the browser-SDK form client-side: wrap it in
<ClientOnly>or callinitinonMounted— the send has to happen in the browser. - Form field
nameattributes map to template variables:name="email"fills{{email}}. - Use
@submit.prevent— forgetting.preventreloads the page mid-send.
Next
- Browser quickstart — templates,
sendForm, migrating from EmailJS - Frontend email without a backend
- The email API — for the server-route variant
- All docs