How to Build a Beautiful Contact Form with Tailwind CSS

5 min read
Static Forms Team

Tailwind CSS makes it easy to build professional-looking forms without writing custom CSS. Its utility-first approach gives you full control over spacing, colors, typography, and responsive behavior directly in your HTML. Combined with Static Forms for the backend, you can have a beautiful, working contact form in minutes.

In this guide, we will build a Tailwind-styled contact form from scratch, add polish with focus states and hover effects, create a dark mode variant, and wire it up to Static Forms for email delivery.

Basic Contact Form

Here is a clean, minimal contact form styled with Tailwind CSS:

HTML
<form action="https://api.staticforms.dev/submit" method="POST" class="max-w-lg mx-auto p-8">
  <h2 class="text-2xl font-bold text-gray-900 mb-6">Contact Us</h2>

  <div class="mb-4">
    <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 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
    />
  </div>

  <div class="mb-4">
    <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 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
    />
  </div>

  <div class="mb-6">
    <label for="message" class="block text-sm font-medium text-gray-700 mb-1">Message</label>
    <textarea
      id="message"
      name="message"
      rows="4"
      required
      class="w-full px-4 py-2 border border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-y"
    ></textarea>
  </div>

  <!-- Static Forms configuration -->
  <input type="hidden" name="apiKey" value="YOUR_API_KEY_HERE" />
  <input type="hidden" name="replyTo" value="@" />
  <input type="text" name="honeypot" class="hidden" />

  <button
    type="submit"
    class="w-full bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700 transition-colors"
  >
    Send Message
  </button>
</form>

Replace YOUR_API_KEY_HERE with your API key from the Static Forms dashboard. This form submits directly to Static Forms and delivers the message to your inbox.

Polished Version with Enhanced States

Let's improve the form with better visual feedback, a card layout, and error state styling:

HTML
<div class="min-h-screen bg-gray-50 flex items-center justify-center p-4">
  <form
    action="https://api.staticforms.dev/submit"
    method="POST"
    class="w-full max-w-md bg-white rounded-2xl shadow-lg p-8 space-y-5"
  >
    <div>
      <h2 class="text-2xl font-bold text-gray-900">Get in Touch</h2>
      <p class="text-gray-500 mt-1">We would love to hear from you.</p>
    </div>

    <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
        placeholder="Jane Doe"
        class="w-full px-4 py-2.5 border border-gray-300 rounded-lg text-gray-900 placeholder-gray-400
               focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
               hover:border-gray-400 transition-colors
               invalid:border-red-500 invalid:focus:ring-red-500"
      />
    </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
        placeholder="jane@example.com"
        class="w-full px-4 py-2.5 border border-gray-300 rounded-lg text-gray-900 placeholder-gray-400
               focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
               hover:border-gray-400 transition-colors
               invalid:border-red-500 invalid:focus:ring-red-500"
      />
    </div>

    <div>
      <label for="subject" class="block text-sm font-medium text-gray-700 mb-1">Subject</label>
      <input
        type="text"
        id="subject"
        name="subject"
        placeholder="What is this about?"
        class="w-full px-4 py-2.5 border border-gray-300 rounded-lg text-gray-900 placeholder-gray-400
               focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
               hover:border-gray-400 transition-colors"
      />
    </div>

    <div>
      <label for="message" class="block text-sm font-medium text-gray-700 mb-1">Message</label>
      <textarea
        id="message"
        name="message"
        rows="4"
        required
        placeholder="Tell us what you need..."
        class="w-full px-4 py-2.5 border border-gray-300 rounded-lg text-gray-900 placeholder-gray-400
               focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
               hover:border-gray-400 transition-colors resize-y
               invalid:border-red-500 invalid:focus:ring-red-500"
      ></textarea>
    </div>

    <input type="hidden" name="apiKey" value="YOUR_API_KEY_HERE" />
    <input type="hidden" name="replyTo" value="@" />
    <input type="text" name="honeypot" class="hidden" />

    <button
      type="submit"
      class="w-full bg-blue-600 text-white py-2.5 px-4 rounded-lg font-medium
             hover:bg-blue-700 active:bg-blue-800
             focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
             transition-colors cursor-pointer
             disabled:bg-gray-400 disabled:cursor-not-allowed"
    >
      Send Message
    </button>
  </form>
</div>

Key details in this version:

  • Hover states on inputs (hover:border-gray-400) give subtle feedback when users mouse over fields
  • Focus rings (focus:ring-2 focus:ring-blue-500) clearly indicate the active field
  • Validation styling (invalid:border-red-500) highlights fields with errors using native HTML validation
  • Active state on the button (active:bg-blue-800) provides click feedback
  • Disabled state (disabled:bg-gray-400) for when the form is submitting

Dark Mode Variant

Tailwind's dark: modifier makes it straightforward to add dark mode support. Wrap the same form with dark mode classes:

HTML
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center p-4">
  <form
    action="https://api.staticforms.dev/submit"
    method="POST"
    class="w-full max-w-md bg-white dark:bg-gray-800 rounded-2xl shadow-lg p-8 space-y-5"
  >
    <div>
      <h2 class="text-2xl font-bold text-gray-900 dark:text-white">Get in Touch</h2>
      <p class="text-gray-500 dark:text-gray-400 mt-1">We would love to hear from you.</p>
    </div>

    <div>
      <label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Name</label>
      <input
        type="text"
        id="name"
        name="name"
        required
        placeholder="Jane Doe"
        class="w-full px-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg
               text-gray-900 dark:text-white bg-white dark:bg-gray-700
               placeholder-gray-400 dark:placeholder-gray-500
               focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
               hover:border-gray-400 dark:hover:border-gray-500 transition-colors"
      />
    </div>

    <div>
      <label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Email</label>
      <input
        type="email"
        id="email"
        name="email"
        required
        placeholder="jane@example.com"
        class="w-full px-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg
               text-gray-900 dark:text-white bg-white dark:bg-gray-700
               placeholder-gray-400 dark:placeholder-gray-500
               focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
               hover:border-gray-400 dark:hover:border-gray-500 transition-colors"
      />
    </div>

    <div>
      <label for="message" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Message</label>
      <textarea
        id="message"
        name="message"
        rows="4"
        required
        placeholder="Tell us what you need..."
        class="w-full px-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg
               text-gray-900 dark:text-white bg-white dark:bg-gray-700
               placeholder-gray-400 dark:placeholder-gray-500
               focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
               hover:border-gray-400 dark:hover:border-gray-500 transition-colors resize-y"
      ></textarea>
    </div>

    <input type="hidden" name="apiKey" value="YOUR_API_KEY_HERE" />
    <input type="hidden" name="replyTo" value="@" />
    <input type="text" name="honeypot" class="hidden" />

    <button
      type="submit"
      class="w-full bg-blue-600 text-white py-2.5 px-4 rounded-lg font-medium
             hover:bg-blue-700 active:bg-blue-800
             focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
             dark:focus:ring-offset-gray-800
             transition-colors cursor-pointer"
    >
      Send Message
    </button>
  </form>
</div>

The dark mode classes adjust backgrounds (dark:bg-gray-800), text colors (dark:text-white), borders (dark:border-gray-600), and the focus ring offset color to match the dark background.

Make sure your Tailwind config has dark mode enabled. In tailwind.config.js, set darkMode: 'class' or darkMode: 'media' depending on whether you want manual toggle or system preference.

Connecting to Static Forms

All the examples above use the standard Static Forms HTML integration. Replace YOUR_API_KEY_HERE with your API key and the form is ready to send emails. For JavaScript-powered submissions with loading states and inline success messages, see our getting started guide.

Wrapping Up

Tailwind CSS and Static Forms are a natural pairing for developers who want good-looking, functional forms without the overhead of a CSS framework or a custom backend. Copy any of the examples above, drop in your API key, and you have a production-ready contact form.

Ready to get started? Sign up for Static Forms and get 500 free submissions per month.