How to Build a Beautiful Contact Form with Tailwind CSS
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:
<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:
<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:
<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.
Related Articles
How to Add a Contact Form to Google Sites
Learn how to add a working contact form to your Google Sites website using Static Forms — no backend or coding experience required.
How to Add a Contact Form to Your Astro Website
Step-by-step tutorial for adding a working contact form to Astro sites using Static Forms — no backend required.
The Complete Guide to Contact Forms on JAMstack Sites
Learn how to add contact forms to JAMstack and static websites without a backend. Works with Next.js, Gatsby, Hugo, Jekyll, Astro, and more.