WordPress contact form to email without a plugin
WordPress can send form mail itself, but it sends from your web server, which is often why those messages land in spam. With FormRoute the form posts to FormRoute instead, and the email comes from your own verified domain. No form plugin and no SMTP plugin are needed.
You need a FormRoute account with a verified domain and a form whose fields are name, email and message, or change the names below to match yours. Getting started covers the setup.
1. Add a Custom HTML block
Edit the page that should hold the form, add a Custom HTML block and paste the form and the script below into it.
Replace fr_your_site_key with the site key from your FormRoute account.
<form id="contact-form">
<label>Name <input name="name" required maxlength="120"></label>
<label>Email <input name="email" type="email" required maxlength="254"></label>
<label>Message <textarea name="message" required maxlength="5000"></textarea></label>
<!-- Honeypot: hidden from people, must stay empty -->
<div aria-hidden="true" style="position:absolute;left:-10000px">
<label>Leave this empty <input name="hp" tabindex="-1" autocomplete="off"></label>
</div>
<button type="submit">Send</button>
<p id="contact-status" role="status"></p>
</form>
<script>
const ENDPOINT = "https://form-route.com/api/v1/send";
const SITE_KEY = "fr_your_site_key"; // from your FormRoute account
const form = document.querySelector("#contact-form");
const status = document.querySelector("#contact-status");
const shownAt = Date.now();
form.addEventListener("submit", async (event) => {
event.preventDefault();
const data = new FormData(form);
const button = form.querySelector("button[type=submit]");
button.disabled = true;
status.textContent = "Sending…";
try {
const response = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
siteKey: SITE_KEY,
t: Date.now() - shownAt, // ms since the form was shown
hp: data.get("hp") ?? "", // honeypot, empty for people
name: data.get("name"),
email: data.get("email"),
message: data.get("message"),
}),
});
if (response.ok) {
form.reset();
status.textContent = "Thanks, your message was sent.";
} else {
status.textContent = "Sorry, that didn't go through. Please try again.";
}
} catch {
status.textContent = "Sorry, that didn't go through. Please try again.";
} finally {
button.disabled = false;
}
});
</script>2. Check that the script was saved
WordPress only keeps <script> tags for users allowed to post unfiltered HTML. On a single site that is administrators; on a multisite network it is super admins only. If the script disappears after saving, ask a super admin to add it, or load it with a code-snippets plugin on that page.
On WordPress.com, check that your plan allows custom code before you start.
3. List your website in FormRoute
In your FormRoute account, open your form and add every address the form is served from to Allowed websites, for example https://www.example.com and https://example.com. Each entry is a scheme and host, with a port only if your site uses one.
A browser submission from an address that isn't listed is refused, and your account shows it as Rejected: website not allowed. For local development, use the exact address your dev server prints.
4. Send a test
Open the page, fill in the form and send it. The submission appears on your FormRoute home page within seconds, as Sent or with the reason it was refused, and the email arrives from your own domain.
Fill the form in at a human pace: a submission sent within 1.2 seconds of the form appearing is treated as automated.
Good to know
- Caching and optimisation plugins can delay or combine scripts. If the form submits as a normal page load, exclude this page's inline script from JavaScript optimisation.
- The form is styled by your theme, like any other HTML on the page.
More guides
HTML · Next.js · React · Vue · Astro · Webflow · Framer · Hugo, Jekyll and Eleventy