ReactForm
Home/Tools/Tailwind Styled Form

React Form with Tailwind Styled Form

Tailwind CSS excels at form styling because utility classes map directly to the states that matter — focus, disabled, hover, and error. The key is defining a consistent set of classes for each field state and reusing them across every input in the form, rather than applying classes ad hoc. This example shows focus rings, transition effects, a disabled submit button during fake submission, and dark-mode-ready classes.

ReactForm Team·May 2026·4 min read

The challenge

Tailwind utility classes can become repetitive and inconsistent in forms without a systematic approach to field styling.

  • Keeping the focus ring consistent across all form elements without writing the same classes repeatedly
  • Disabling the submit button during submission and reflecting that visually with opacity and cursor changes
  • Making hover states on buttons feel responsive without janky transitions
  • Getting dark mode to work on form inputs without the default browser dark mode overriding your styles

Working code example

Here is a complete, self-contained working example you can drop directly into any React project. It uses Tailwind CSS for styling and requires no external dependencies beyond React itself.

TailwindForm.jsx
import { useState } from 'react';

const inputCls = 'w-full border border-gray-300 dark:border-slate-600 rounded-xl px-4 py-2.5 text-sm text-gray-900 dark:text-slate-100 bg-white dark:bg-slate-800 outline-none transition focus:border-teal-500 focus:ring-2 focus:ring-teal-500/30 placeholder:text-gray-400 dark:placeholder:text-slate-500';

const labelCls = 'block text-sm font-medium text-gray-700 dark:text-slate-300 mb-1.5';

export default function TailwindForm() {
  const [form, setForm] = useState({ name: '', email: '', message: '', agree: false });
  const [submitting, setSubmitting] = useState(false);
  const [done, setDone] = useState(false);

  const handle = (e) => {
    const { name, value, type, checked } = e.target;
    setForm((f) => ({ ...f, [name]: type === 'checkbox' ? checked : value }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    setSubmitting(true);
    setTimeout(() => {
      setSubmitting(false);
      setDone(true);
    }, 1800);
  };

  if (done)
    return (
      <div className="max-w-md mx-auto p-6 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-2xl text-green-700 dark:text-green-300">
        <p className="font-semibold">Message sent!</p>
        <p className="text-sm mt-1 opacity-80">We'll get back to you within 24 hours.</p>
      </div>
    );

  return (
    <form onSubmit={handleSubmit}
      className="max-w-md mx-auto p-6 bg-white dark:bg-slate-900 rounded-2xl shadow-lg border border-gray-100 dark:border-slate-700 space-y-5">
      <h2 className="text-xl font-bold text-gray-900 dark:text-slate-100">Get in touch</h2>
      <div>
        <label className={labelCls}>Your name</label>
        <input name="name" value={form.name} onChange={handle}
          placeholder="Jane Smith" className={inputCls} />
      </div>
      <div>
        <label className={labelCls}>Email address</label>
        <input name="email" type="email" value={form.email} onChange={handle}
          placeholder="jane@example.com" className={inputCls} />
      </div>
      <div>
        <label className={labelCls}>Message</label>
        <textarea name="message" value={form.message} onChange={handle}
          rows={4} style={{ resize: 'none' }} placeholder="How can we help?"
          className={inputCls} />
      </div>
      <label className="flex items-start gap-3 cursor-pointer group">
        <input name="agree" type="checkbox" checked={form.agree} onChange={handle}
          className="w-4 h-4 mt-0.5 accent-teal-600 rounded" />
        <span className="text-sm text-gray-600 dark:text-slate-400 group-hover:text-gray-800 dark:group-hover:text-slate-200 transition">
          I agree to the privacy policy and terms of service
        </span>
      </label>
      <button type="submit" disabled={submitting || !form.agree}
        className="w-full bg-teal-600 hover:bg-teal-700 text-white font-semibold py-2.5 rounded-xl shadow transition disabled:opacity-50 disabled:cursor-not-allowed active:scale-[0.98]">
        {submitting ? 'Sending…' : 'Send Message'}
      </button>
    </form>
  );
}

How ReactForm.co helps

ReactForm's visual builder handles all of the above — tailwind styled form configuration, validation rules, state management, and responsive layout — without writing a single line of code. Drag fields onto the canvas, configure their properties in the sidebar, and get production-ready React output. You can publish the form and collect responses instantly, or export the JSX to drop into your own codebase.

Build this form visually

Frequently asked questions

How do I add a focus ring to form inputs with Tailwind?

Add focus:ring-2 focus:ring-teal-500 and outline-none to the input element. The outline-none removes the default browser focus outline and focus:ring-2 replaces it with a Tailwind ring. Add focus:ring-teal-500/30 for a soft semi-transparent ring color. This pattern works the same for inputs, textareas, and selects.

How do I disable a button while a form is submitting in React?

Keep a submitting boolean in state, set it to true when submit starts, and back to false when done. Pass it to the button: disabled={submitting}. Add disabled:opacity-50 disabled:cursor-not-allowed to the button's className so the disabled state is visually obvious. Change the button text to show progress: {submitting ? "Saving…" : "Save"}.

How do I make Tailwind form inputs work in dark mode?

Add dark: variants for background color (dark:bg-slate-800), border (dark:border-slate-600), and text (dark:text-slate-100). The default browser form styles for dark mode can look jarring — explicitly setting these classes ensures consistent appearance regardless of OS preference. Make sure your Tailwind config has darkMode: "class" or "media" set.

Related topics

Build this form visually — no code needed

ReactForm.co handles tailwind styled form fields, validation, conditional logic, and responsive layout automatically. Publish in minutes and collect responses for free.