Add a fully functional contact form to any HTML website in under 5 minutes. No JavaScript frameworks, no server setup — just plain HTML that works everywhere.
TL;DR
To add a form to any HTML website, point your form's action attribute to a FormsList endpoint at `https://formslist.com/f/your-form-id`. FormsList handles submission storage, email notifications, and spam filtering — no server-side code or JavaScript required.
Create a free FormsList account and add a new form. FormsList gives you a unique endpoint URL that you can use as the action attribute of any HTML form. Copy the URL and keep it handy for the next step. When you create a form in FormsList, you get a unique URL like https://formslist.com/f/abc123. This URL acts as a lightweight backend that accepts POST requests, stores the submitted data, and triggers any notifications or integrations you have configured. The endpoint works with any HTML page — whether it is a single-page website hosted on GitHub Pages, a landing page on Netlify, a client site on shared hosting, or even an HTML file opened locally on your computer during development. Before moving on, take a moment to configure your form settings in the FormsList dashboard. Give it a descriptive name so you can identify it later, enter the email address where you want to receive submissions, and optionally set a custom redirect URL where users will be sent after submitting the form. If you do not set a redirect URL, FormsList displays a default thank-you page.
Paste the following form markup into your HTML file wherever you want the form to appear. The form uses the standard POST method and points directly at your FormsList endpoint. When a visitor submits the form, FormsList processes the data and redirects the user back to your site. Every input element must have a name attribute — this is the key that identifies each field in the submission data. The name attribute values appear as column headers in your FormsList dashboard and as field labels in email notifications. Use clear, descriptive names like "name", "email", and "message" rather than cryptic identifiers. The id attribute is separate from name and is used for linking labels to inputs with the for attribute, which is important for accessibility. The required attribute on inputs ensures the browser will not submit the form until those fields are filled in. The type="email" attribute provides built-in email format validation — the browser checks that the value contains an @ symbol and a domain before allowing submission. These HTML5 validation attributes give you basic form validation with zero JavaScript, which is one of the advantages of using plain HTML forms.
<form action="https://formslist.com/f/YOUR_FORM_HASH" method="POST">
<label for="name">Name</label>
<input type="text" id="name" name="name" required />
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
<label for="message">Message</label>
<textarea id="message" name="message" rows="4" required></textarea>
<button type="submit">Send</button>
</form>An unstyled HTML form looks bare and unprofessional. Adding CSS transforms it into a polished component that matches your website's design. The CSS below provides a clean, modern look that works as a solid starting point. You can customize the colors, fonts, and spacing to match your brand. The most important CSS rules for forms are: set box-sizing:border-box on inputs so padding does not cause them to overflow their container, make inputs full-width with width:100% so the form looks consistent on all screen sizes, add comfortable padding inside inputs (at least 0.5rem) so they are easy to tap on mobile devices, and use a visible focus state (outline or box-shadow) so keyboard users can see which field is active. The :focus-visible pseudo-class is better than :focus for this because it only shows the focus ring for keyboard navigation, not for mouse clicks. For responsive design, wrap the form in a container with max-width and add horizontal padding. This prevents the form from stretching too wide on desktop screens while still using the full width on mobile. A max-width of 480px to 560px works well for most contact forms. Test the form on a phone to make sure the inputs are large enough to tap comfortably — Apple's Human Interface Guidelines recommend a minimum touch target of 44x44 points.
<style>
form {
max-width: 480px;
margin: 2rem auto;
padding: 0 1rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
label {
display: block;
margin-top: 1rem;
font-weight: 600;
font-size: 0.95rem;
color: #374151;
}
input, textarea {
width: 100%;
padding: 0.6rem 0.75rem;
margin-top: 0.25rem;
border: 1px solid #d1d5db;
border-radius: 6px;
box-sizing: border-box;
font-size: 1rem;
transition: border-color 0.15s, box-shadow 0.15s;
}
input:focus, textarea:focus {
outline: none;
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
}
button {
margin-top: 1.5rem;
padding: 0.6rem 1.5rem;
background: #2563eb;
color: #fff;
border: none;
border-radius: 6px;
font-size: 1rem;
cursor: pointer;
transition: background 0.15s;
}
button:hover {
background: #1d4ed8;
}
button:focus-visible {
outline: 2px solid #2563eb;
outline-offset: 2px;
}
</style>While HTML5 validation attributes handle the basics, adding JavaScript validation gives you more control over error messages and the user experience. You can validate fields as the user fills them out (on blur) rather than waiting until they click Submit, and you can show custom error messages that are more helpful than the browser defaults. The example below attaches blur event listeners to each input. When the user leaves a field, the script checks whether the value is valid using the built-in checkValidity() method, which respects all HTML5 validation attributes you have already set. If the field is invalid, a custom error message appears below it. When the user corrects the field, the error disappears. This approach builds on top of your HTML5 attributes rather than replacing them, so you get the best of both worlds. For forms that need more advanced validation — like checking that a phone number matches a specific format, or that two fields match (confirm email), or that a field value exists in a predefined list — write custom validation functions and call them in the blur handler. Keep error messages specific and actionable: instead of "Invalid input", say "Please enter a valid email address (e.g., you@example.com)". The user should always know exactly what to fix.
<script>
const form = document.querySelector("form");
const inputs = form.querySelectorAll("input, textarea");
function showError(field, message) {
clearError(field);
const error = document.createElement("span");
error.className = "field-error";
error.style.cssText = "display:block;color:#dc2626;font-size:0.85rem;margin-top:0.25rem;";
error.textContent = message;
field.after(error);
field.style.borderColor = "#dc2626";
}
function clearError(field) {
const next = field.nextElementSibling;
if (next && next.classList.contains("field-error")) next.remove();
field.style.borderColor = "#d1d5db";
}
inputs.forEach((input) => {
input.addEventListener("blur", () => {
if (!input.checkValidity()) {
const messages = {
name: "Please enter your name (at least 2 characters).",
email: "Please enter a valid email address.",
message: "Please enter a message.",
};
showError(input, messages[input.name] || input.validationMessage);
} else {
clearError(input);
}
});
input.addEventListener("input", () => {
if (input.validity.valid) clearError(input);
});
});
</script>By default, when a plain HTML form submits, the browser navigates away from your page to the FormsList endpoint, which then redirects the user back. This works fine but can feel jarring because the page reloads. For a smoother experience, you can use JavaScript to submit the form via AJAX (fetch) and display the result inline without leaving the page. The fetch-based approach intercepts the form submit event, prevents the default browser behavior, sends the form data in the background, and then shows a success or error message on the same page. This is the approach most modern websites use because it keeps the user in context. After a successful submission, reset the form fields and display a green success message. After an error, show a red error message and keep the form data intact so the user does not have to re-enter everything. Another option is to set a custom redirect URL in your FormsList dashboard. After submission, FormsList redirects the user to the URL you specify — typically a dedicated thank-you page on your site. This is simpler than the fetch approach because it requires no JavaScript, and having a separate thank-you page allows you to track conversions in Google Analytics by measuring visits to that page. Choose the approach that fits your needs: AJAX for a seamless in-page experience, or redirect for simplicity and conversion tracking.
<!-- Option 1: AJAX submission (stays on page) -->
<script>
const form = document.querySelector("form");
form.addEventListener("submit", async (e) => {
e.preventDefault();
const btn = form.querySelector("button");
btn.disabled = true;
btn.textContent = "Sending...";
try {
const res = await fetch(form.action, {
method: "POST",
body: new FormData(form),
});
if (res.ok) {
form.reset();
form.insertAdjacentHTML("afterend",
'<p style="color:#16a34a;font-weight:600;">Thank you! Your message has been sent.</p>');
btn.textContent = "Sent!";
} else {
btn.textContent = "Send";
btn.disabled = false;
alert("Something went wrong. Please try again.");
}
} catch {
btn.textContent = "Send";
btn.disabled = false;
alert("Network error. Please check your connection.");
}
});
</script>
<!-- Option 2: Set a redirect URL in FormsList dashboard -->
<!-- After submission, the user is redirected to your thank-you page -->
<!-- No JavaScript required for this approach -->Submit a test entry through your form and check the FormsList dashboard to confirm the submission arrived. Verify that all field values are captured correctly and that the field names in the dashboard match what you expect. Enable email notifications so every future submission is forwarded to your inbox automatically. Test edge cases too: submit the form with the minimum required data, with very long input values, and with special characters like accents, quotes, and angle brackets. Make sure the form works on mobile devices by testing on a real phone, not just a resized browser window — mobile browsers sometimes handle forms differently, especially with keyboard types and auto-fill behavior. Check that the form is accessible by tabbing through all fields with the keyboard and submitting without using a mouse. Once you are satisfied the form works correctly, go live. Monitor the first few real submissions in the FormsList dashboard to confirm everything is flowing through properly. If you notice spam submissions, enable the built-in honeypot detection and AI spam filtering in your form settings. For high-traffic forms, consider adding reCAPTCHA for additional protection. FormsList also provides submission analytics so you can track volume trends and see where your submissions are coming from geographically.
Learn how to process form submissions on any website without writing server-side code. Use a form backend service to receive, store, and forward submissions by email.
Learn moreProtect your form from spam and bots by adding Google reCAPTCHA. This guide covers both reCAPTCHA v2 (checkbox) and v3 (invisible) with step-by-step instructions.
Learn moreAdd a fully functional contact form to any static site generator — Jekyll, Hugo, Eleventy, Astro, or plain HTML. No server-side code required.
Learn moreSet up your form backend in under a minute. No server required, no complex configuration — just a simple endpoint for your forms.