Guides

Bolt.new contact form that sends email (no backend)

Bolt’s own email setup needs a Bolt database, server functions, a custom domain and a verified sending domain. A contact form needs none of that: the browser posts to FormSMTP, and the email goes out through your own mailbox.

Last updated November 2, 2026

How Bolt handles email

In Bolt, email is sent from server functions, which read an email service’s API key from your database’s Secrets. Bolt’s email guide lists three prerequisites: a Bolt database, Secrets set up in its settings, and a published project with a custom domain attached. Custom domains are a Pro feature. If you ask for email without naming a provider, Bolt usually picks Resend, and you then verify your domain with Resend’s DNS records.

Hosting matters too. New projects publish to Bolt hosting at a free .bolt.host address. You can publish to Netlify instead, but Bolt notes that Netlify can’t host a database or a traditional backend, so the server function route isn’t available there.

RouteGoodNot so good
Server function + email APICan email anyone, from your verified domainNeeds a Bolt database, a custom domain (Pro) and DNS setup. Not on Netlify.
FormSMTP form endpointWorks on .bolt.host and Netlify, sends from the mailbox you already haveContact forms only email you. To email your users, use the server route below.

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. Any mailbox with SMTP access works: Gmail or Google Workspace, Microsoft 365, Zoho, your web host’s mailbox, or a sending service. Personal Outlook.com and Hotmail addresses can’t be used, because Microsoft only lets them sign in with OAuth.

2Lock it to your site

In Settings → Security, set Allowed websites to every address your app is published at, each written out in full: https://your-app.bolt.host, https://your-site.netlify.app if you publish there, and your custom domain, and Allowed recipients to your own address. Then copy the form key from API & code. It starts with fk_, it only works on the form endpoint, and it can only email you, so it’s safe in your page.

The preview inside the Bolt editor runs on a different address. Test on the published URL, or temporarily add the address shown in the error.

3Paste this prompt into Bolt

Replace fk_YOUR_FORM_KEY with your form key:

text
Add a contact form that emails submissions to me through FormSMTP.

- The form key is fk_YOUR_FORM_KEY. It's a public key made for browser
  code, so it can live in the frontend. Store it as a constant (or a
  VITE_ variable if this is a Vite project).
- Create a ContactForm component with name, email and message fields
  that matches the rest of the site's design.
- On submit, POST JSON to
  https://app.formsmtp.com/api/v1/form/ + the form key
  with the headers Content-Type: application/json and
  Accept: application/json. Include the fields plus
  _subject: "New message from my Bolt app" and
  _t: the time the form first rendered (Date.now()).
- Add a hidden honeypot input named _gotcha (display none, tabIndex -1,
  autoComplete off) and send its value with the rest.
- The response is { success: true } or { success: false, error }.
  Disable the button while sending, show a thank-you message on success
  and the error text on failure.
- Don't add a database, server function or secret for this.
- Put the form on the contact page.

4Or paste the component yourself

For a React project (check package.json), this is the whole component. On another framework, ask Bolt to port it: the request stays the same.

jsx
// src/components/ContactForm.jsx
import { useRef, useState } from "react";

// The form key is public: it can only send submissions to you.
const ENDPOINT = "https://app.formsmtp.com/api/v1/form/fk_YOUR_FORM_KEY";

export default function ContactForm() {
  const [status, setStatus] = useState("idle"); // idle | sending | sent | error
  const [error, setError] = useState("");
  const loadedAt = useRef(Date.now());

  async function handleSubmit(e) {
    e.preventDefault();
    const fields = Object.fromEntries(new FormData(e.currentTarget));
    setStatus("sending");
    try {
      const res = await fetch(ENDPOINT, {
        method: "POST",
        headers: { "Content-Type": "application/json", Accept: "application/json" },
        body: JSON.stringify({ ...fields, _subject: "New message from my Bolt app", _t: loadedAt.current }),
      });
      const data = await res.json();
      if (!data.success) throw new Error(data.error);
      setStatus("sent");
    } catch (err) {
      setError(err.message);
      setStatus("error");
    }
  }

  if (status === "sent") return <p>Thanks! We'll be in touch soon.</p>;

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="Name" autoComplete="name" required />
      <input name="email" type="email" placeholder="Email" autoComplete="email" required />
      <textarea name="message" placeholder="Message" rows={5} required />
      <input name="_gotcha" style={{ display: "none" }} tabIndex={-1} autoComplete="off" />
      <button disabled={status === "sending"}>{status === "sending" ? "Sending…" : "Send"}</button>
      {status === "error" && <p role="alert">{error}</p>}
    </form>
  );
}

5Publish and test

Click Publish. To use Netlify instead of Bolt hosting, connect your Netlify account under All project settings → Applications, then pick Netlify in Domains & Hosting → Hosting Provider before your first publish. Submit the form on the live URL: the message arrives from your own address and appears under Logs in FormSMTP.

Emailing your users from a server function

For emails to other people, like order confirmations, call the send API from a Bolt server function with your secret API key. This needs a Bolt database (for Secrets and server functions) and Bolt hosting, not Netlify.

text
When an order is placed, email the customer a confirmation through the
FormSMTP API from a server function.

- Read the API key from a secret named FORMSMTP_API_KEY and ask me to
  add it in the database Secrets.
- POST https://app.formsmtp.com/api/v1/send with the header
  Authorization: Bearer FORMSMTP_API_KEY and a JSON body with
  to, subject, html and replyTo.
- Take the recipient and order details from the database, not from
  the request body.
- The response is { success: true, messageId } or
  { success: false, error }. Log the error if it fails.

Add the secret yourself under the database icon → Secrets; secret names are case-sensitive. Use a separate FormSMTP project for customer email so your contact form project stays locked to your own inbox.

Troubleshooting

  • “This form isn’t allowed on …”. Add the exact address from the error. The editor preview, your .bolt.host address, your netlify.app address and your custom domain are all different origins.
  • It broke after you renamed the Netlify URL. Update Allowed websites to the new netlify.app address.
  • “Too many submissions”. You hit the per-visitor limit while testing: 5 a minute, and 10 an hour by default. Wait, or raise the hourly number in Settings → Spam protection.
  • “Recipient not allowed for this project”. The server function emails customers, but Allowed recipients is set to your address. Use a separate project for customer email.

FAQ

Do I need a Bolt database to send email?

Not for a contact form. The browser posts straight to FormSMTP. You only need a database, for Secrets and server functions, if your app emails people other than you.

Does it work on the free plan?

Bolt lets Free and Pro users publish to a .bolt.host address, and the form works there. FormSMTP’s free plan includes 1,000 emails a month.

Does it work on Netlify?

Yes. The form is front-end code that posts to FormSMTP over HTTPS, so it doesn’t need the backend Netlify can’t host. Add your netlify.app address to Allowed websites.

Can the visitor get a confirmation email?

Not from the form endpoint: it only emails you, so a copied form key can’t be used to spam other people. To email the visitor, call the send API with your API key from server code (see the server function section above).

Is the form key safe in my page?

Yes, it’s made for that. The form key (fk_) only works on the form endpoint and always delivers to your own inbox, with per-visitor limits and a spam filter on top. Someone who copies it can’t email anyone else. The API key (fs_) can, so keep that one in server code or a secrets store.

More guides

Get a Founding Pass

10,000 emails a month, free for your first year. Pass No. 0008 is next: 1,993 of 2,000 left, no card.