How to Build a Contact Form with Tailwind CSS

Last updated: March 2026

Tailwind CSS makes it easy to build clean, responsive forms with utility classes. But styling is only half the problem: you still need a backend to process submissions. This guide shows how to build a polished Tailwind CSS contact form and connect it to FormWit so it actually sends emails.

No server code, no API routes, no database setup. Just a beautiful form that works.

What you'll build

By the end of this guide you'll have:

  • A responsive contact form styled entirely with Tailwind CSS utility classes
  • Proper label/input styling with focus states and smooth transitions
  • A dropdown select field for categorizing inquiries
  • Inline validation feedback using Tailwind's peer classes
  • A honeypot field for spam protection
  • A working backend: when someone submits the form, you receive an email notification

The form connects to FormWit's serverless form backend. No server-side code required.

Build the Tailwind contact form

Step 1: Create a FormWit account and get your endpoint

Go to app.formwit.com/auth/signup and create a free account. No credit card required.

In your dashboard, click Create Form and give it a name (e.g., "Contact Form"). You'll get a unique endpoint URL that looks like this:

https://app.formwit.com/api/s/YOUR_FORM_ID

Copy that URL. You'll use it in the next step.

Step 2: Create the form HTML with Tailwind classes

Paste this into your HTML file. Replace YOUR_FORM_ID with the endpoint URL from your FormWit dashboard.

<form action="https://app.formwit.com/api/s/YOUR_FORM_ID" method="POST"
  class="max-w-lg mx-auto space-y-6 p-8">

  <h2 class="text-2xl font-bold text-gray-900">Contact Us</h2>
  <p class="text-gray-600">Fill out the form below and we'll get back to you.</p>

  <div>
    <label for="name" class="block text-sm font-medium text-gray-700 mb-1">Name</label>
    <input type="text" id="name" name="name" required
      class="w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm
             focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition"
      placeholder="Your name" />
  </div>

  <div>
    <label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email</label>
    <input type="email" id="email" name="email" required
      class="w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm
             focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition"
      placeholder="you@example.com" />
  </div>

  <div>
    <label for="subject" class="block text-sm font-medium text-gray-700 mb-1">Subject</label>
    <select id="subject" name="subject" required
      class="w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm
             focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition">
      <option value="">Select a topic...</option>
      <option value="General">General Inquiry</option>
      <option value="Support">Support</option>
      <option value="Sales">Sales</option>
    </select>
  </div>

  <div>
    <label for="message" class="block text-sm font-medium text-gray-700 mb-1">Message</label>
    <textarea id="message" name="message" rows="5" required
      class="w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm
             focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition resize-y"
      placeholder="Your message..."></textarea>
  </div>

  <!-- Honeypot spam protection -->
  <input type="text" name="_gotcha" class="hidden" tabindex="-1" autocomplete="off" />

  <button type="submit"
    class="w-full py-3 px-6 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold
           rounded-lg shadow-sm transition focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
    Send Message
  </button>
</form>

Here's what each Tailwind class is doing:

  • max-w-lg mx-auto - centers the form with a max width of 32rem
  • space-y-6 - adds consistent vertical spacing between fields
  • focus:ring-2 focus:ring-indigo-500 - adds a visible focus ring for accessibility
  • rounded-lg shadow-sm - subtle rounded corners and shadow on inputs
  • transition - smooth animation on hover and focus states
  • hidden on the honeypot - hides the spam trap from real users while bots fill it in

Step 3: Add JavaScript for AJAX submission (optional)

By default, the form does a full page redirect after submission. If you want to keep the user on the same page, add this script:

const form = document.querySelector('form');

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const button = form.querySelector('button[type="submit"]');
  button.textContent = 'Sending...';
  button.disabled = true;

  const response = await fetch(form.action, {
    method: 'POST',
    body: new FormData(form),
  });

  if (response.ok) {
    form.innerHTML = '<p class="text-center text-green-600 font-semibold py-8">Message sent! We\'ll be in touch.</p>';
  } else {
    button.textContent = 'Send Message';
    button.disabled = false;
    alert('Something went wrong. Please try again.');
  }
});

This disables the button while sending and replaces the form with a success message on completion.

Step 4: Test it

Open your page in a browser, fill out the form, and submit. You'll see the submission appear in your FormWit dashboard and receive an email notification within seconds.

Dark mode variant

If your site supports dark mode, Tailwind's dark: prefix makes it straightforward to add a dark variant. Here's the same form adapted for dark backgrounds:

<form action="https://app.formwit.com/api/s/YOUR_FORM_ID" method="POST"
  class="max-w-lg mx-auto space-y-6 p-8 bg-gray-900 rounded-2xl">

  <h2 class="text-2xl font-bold text-white">Contact Us</h2>
  <p class="text-gray-400">Fill out the form below and we'll get back to you.</p>

  <div>
    <label for="name" class="block text-sm font-medium text-gray-300 mb-1">Name</label>
    <input type="text" id="name" name="name" required
      class="w-full px-4 py-2.5 bg-gray-800 border border-gray-700 text-white rounded-lg
             focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition
             placeholder-gray-500"
      placeholder="Your name" />
  </div>

  <div>
    <label for="email" class="block text-sm font-medium text-gray-300 mb-1">Email</label>
    <input type="email" id="email" name="email" required
      class="w-full px-4 py-2.5 bg-gray-800 border border-gray-700 text-white rounded-lg
             focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition
             placeholder-gray-500"
      placeholder="you@example.com" />
  </div>

  <div>
    <label for="message" class="block text-sm font-medium text-gray-300 mb-1">Message</label>
    <textarea id="message" name="message" rows="5" required
      class="w-full px-4 py-2.5 bg-gray-800 border border-gray-700 text-white rounded-lg
             focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition resize-y
             placeholder-gray-500"
      placeholder="Your message..."></textarea>
  </div>

  <input type="text" name="_gotcha" class="hidden" tabindex="-1" autocomplete="off" />

  <button type="submit"
    class="w-full py-3 px-6 bg-indigo-600 hover:bg-indigo-500 text-white font-semibold
           rounded-lg transition focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-900
           focus:ring-indigo-500">
    Send Message
  </button>
</form>

Key changes: bg-gray-900 on the form wrapper, bg-gray-800 and border-gray-700 on inputs, text-white and text-gray-300 for text, and placeholder-gray-500 for muted placeholder text. The focus:ring-offset-gray-900 on the button ensures the focus ring looks right against the dark background.

Making it responsive

The form is already responsive out of the box. max-w-lg constrains the width on large screens, while w-full on every input ensures fields stretch to fill the container on small screens. No media queries needed.

If you want a two-column layout on larger screens (for example, name and email side by side), wrap those fields in a grid:

<div class="grid grid-cols-1 sm:grid-cols-2 gap-6">
  <div>
    <label for="name" class="block text-sm font-medium text-gray-700 mb-1">Name</label>
    <input type="text" id="name" name="name" required
      class="w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm
             focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition"
      placeholder="Your name" />
  </div>

  <div>
    <label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email</label>
    <input type="email" id="email" name="email" required
      class="w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm
             focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition"
      placeholder="you@example.com" />
  </div>
</div>

On screens smaller than 640px (sm: breakpoint), the grid stacks to a single column. On larger screens, the two fields sit side by side. The rest of the form remains full-width below.

Tailwind form validation styles

HTML5 validation handles the logic (required fields, email format, etc.), but the default browser validation popups look inconsistent across browsers. Tailwind's peer utility lets you show inline validation messages with pure CSS. No JavaScript required.

Here's how it works:

<div>
  <label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email</label>
  <input type="email" id="email" name="email" required
    class="peer w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm
           focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition"
    placeholder="you@example.com" />
  <p class="hidden peer-invalid:block text-sm text-red-500 mt-1">
    Please enter a valid email address.
  </p>
</div>

The peer class on the input creates a relationship with the sibling <p> element. The validation message is hidden by default (hidden) and only appears when the input is in an invalid state (peer-invalid:block). This works for any HTML5 validation: required, type="email", minlength, pattern, and more.

You can also style the input border on invalid state:

<input type="email" name="email" required
  class="peer w-full px-4 py-2.5 border border-gray-300 rounded-lg
         focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500
         invalid:border-red-500 invalid:focus:ring-red-500 transition" />

This turns the border and focus ring red when the field is invalid, giving users immediate visual feedback.

Summary

Tailwind CSS gives you full control over form styling without writing custom CSS. Combined with a serverless form backend like FormWit, you get a professional contact form that looks great and actually works. No backend code, no server setup, no email configuration.

Everything in this guide is copy-paste ready. Grab the code, swap in your FormWit endpoint URL, and you have a production-ready contact form.

FormWit's free plan includes unlimited forms, 100 submissions per month, email notifications, and built-in spam protection. Get started free.

Related guides: HTML contact form · Simple contact form · Embed a contact form · Spam protection · Form templates

Frequently asked questions

Can I style FormWit forms with Tailwind?

Yes. FormWit forms are standard HTML, so Tailwind utility classes work directly on all form elements. There is no FormWit-specific markup or CSS to override. Style inputs, labels, buttons, and the form wrapper with any Tailwind classes you want.

What Tailwind classes work best for forms?

Key classes: w-full for full-width inputs, px-4 py-2.5 for comfortable padding, border border-gray-300 rounded-lg for borders, focus:ring-2 focus:ring-indigo-500 for accessible focus states, and space-y-6 on the form for consistent vertical spacing between fields.

Does Tailwind affect form submission?

No. Tailwind is a CSS utility framework that only affects visual styling. It has zero impact on form submission behavior. The form submits the same data to FormWit regardless of what CSS classes you use. Styling and functionality are completely independent.

Want to skip the setup?

FormWit gives you a form endpoint in 60 seconds. Free plan, no credit card.

Create Free Form

Need a form fast?

Build one visually with our free HTML form generator — no coding required.

Try the Form Generator →

Style your FormWit form with Tailwind

Add a contact form to your site in 30 seconds. No backend code required.

Try FormWit Free