# Send email with React

> Send email from a React app with Email Fast: the browser SDK with a public ef_pub_ key and a server-locked template, or a small backend route — both shown.

Canonical: https://emailfast.dev/send-email-with/react

A React component can send email without any 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 it with your public key, point it at a server-stored
template, wire it to a form. If you already run a backend, skip the browser SDK and
send from there instead — that variant is below.

```bash
npm install @email-fast/browser
```

```jsx
// ContactForm.jsx
import { useState } from "react";
import emailfast from "@email-fast/browser";

emailfast.init({ publicKey: "ef_pub_..." }); // public by design — safe to ship

export function ContactForm() {
  const [state, setState] = useState("idle");

  async function onSubmit(e) {
    e.preventDefault();
    setState("sending");
    try {
      await emailfast.sendForm("default", "contact_form", e.target);
      setState("sent");
    } catch {
      setState("error");
    }
  }

  return (
    <form onSubmit={onSubmit}>
      <input name="name" required />
      <input name="email" type="email" required />
      <textarea name="message" required />
      <button disabled={state === "sending"}>Send</button>
      {state === "sent" && <p>Thanks — we got it.</p>}
      {state === "error" && <p>That didn't go through. Try again.</p>}
    </form>
  );
}
```

`"contact_form"` is a template slug: the template — including who receives the mail —
lives in your dashboard, not in the component.

## Or: send through your own backend

If your React app already talks to a server, keep the secret key there and expose one route:

```js
// server.js — Express; the secret key never reaches the browser
import express from "express";
import { EmailFast } from "@email-fast/nodejs";

const app = express().use(express.json());
const ef = new EmailFast({ apiKey: process.env.EMAILFAST_API_KEY, baseUrl: "https://api.emailfast.dev" }); // ef_sandbox_… while testing

app.post("/api/contact", async (req, res) => {
  try {
    const { id } = await ef.send({
      to: "team@yourdomain.dev",
      subject: "New contact message",
      html: "<p>{{message}}</p>",
      data: { message: req.body.message },
      idempotency_key: req.body.formId, // generated once per render — a double-submit replays
    });
    res.status(202).json({ id });
  } catch {
    res.status(502).json({ error: "send failed" });
  }
});
```

## Gotchas

- Never put an `ef_live_` secret key in React code. Bundlers inline every env var you reference — `VITE_…` and `REACT_APP_…` are configuration, not secrets. The only key that may ship in a bundle is `ef_pub_`.
- Form field `name` attributes become template variables: `name="email"` fills `{{email}}` in the template.
- Disable the submit button while sending — `sendForm` reads the form at call time, and the browser call has no idempotency key to dedupe a double click.
- Changing the recipient is a dashboard change, not a code change. That is the point.

## Next

- [Browser quickstart](/docs/quickstart-browser) — templates, `sendForm`, migrating from EmailJS
- [Frontend email without a backend](/features/frontend)
- [The email API](/features/email-api) — for the backend variant
- [All docs](/docs)
