ReactForm
Home/Tools/Radio Buttons

React Form with Radio Buttons

Radio buttons represent a mutually exclusive choice from a fixed set of options, making them ideal for plan selectors, preference pickers, and configuration screens. Styling custom radio buttons with visual card-style selection — where the entire card highlights on selection — is a common pattern that requires no custom checkbox input, just conditional class names.

ReactForm Team·May 2026·3 min read

The challenge

Radio button groups require careful state management and accessibility handling that is easy to get wrong without a clear pattern.

  • Hiding the native radio input and building a fully clickable styled card without breaking keyboard accessibility
  • Ensuring the correct option is pre-selected when the form loads with existing data
  • Communicating the selected state visually without relying solely on the radio dot, which is hard to style cross-browser
  • Grouping radio inputs with the same name attribute so the browser enforces the single-selection constraint

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.

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

const PLANS = [
  { id: 'free', label: 'Free', price: '$0/mo', description: 'Up to 3 forms, 100 responses/month' },
  { id: 'pro', label: 'Pro', price: '$12/mo', description: 'Unlimited forms, 10,000 responses/month' },
  { id: 'enterprise', label: 'Enterprise', price: '$49/mo', description: 'Custom limits, SLA, priority support' },
];

export default function PlanSelector() {
  const [selected, setSelected] = useState('free');
  const [submitted, setSubmitted] = useState(false);

  const handleSubmit = (e) => {
    e.preventDefault();
    setSubmitted(true);
  };

  if (submitted) {
    const plan = PLANS.find((p) => p.id === selected);
    return (
      <div className="max-w-sm mx-auto p-6 bg-green-50 rounded-2xl text-green-700">
        <p className="font-semibold">Plan selected: {plan.label}</p>
        <p className="text-sm mt-1">{plan.price}</p>
      </div>
    );
  }

  return (
    <form onSubmit={handleSubmit} className="max-w-sm mx-auto p-6 bg-white rounded-2xl shadow space-y-4">
      <h2 className="text-xl font-bold text-gray-800">Choose a plan</h2>
      <div className="space-y-3">
        {PLANS.map((plan) => (
          <label key={plan.id}
            className={`flex items-start gap-3 p-4 rounded-xl border-2 cursor-pointer transition
              ${selected === plan.id ? 'border-teal-500 bg-teal-50' : 'border-gray-200 hover:border-gray-300'}`}>
            <input
              type="radio"
              name="plan"
              value={plan.id}
              checked={selected === plan.id}
              onChange={() => setSelected(plan.id)}
              className="mt-1 accent-teal-600"
            />
            <div className="flex-1">
              <div className="flex items-center justify-between">
                <span className="font-semibold text-gray-800">{plan.label}</span>
                <span className="text-sm font-bold text-teal-700">{plan.price}</span>
              </div>
              <p className="text-xs text-gray-500 mt-0.5">{plan.description}</p>
            </div>
          </label>
        ))}
      </div>
      <button type="submit"
        className="w-full bg-teal-600 text-white font-semibold py-2 rounded-lg hover:bg-teal-700 transition">
        Continue with {PLANS.find((p) => p.id === selected)?.label}
      </button>
    </form>
  );
}

How ReactForm.co helps

ReactForm's visual builder handles all of the above — radio buttons 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 control radio buttons in React?

Store the selected value in useState as a string. Set checked={selected === option.value} on each radio input and onChange={() => setSelected(option.value)}. This keeps React as the source of truth and ensures only one option can be selected at a time. Do not use the name attribute alone to enforce single selection in controlled React components.

Can I wrap the entire radio card in a label element?

Yes, and this is the recommended approach. Wrapping the input and its description text in a <label> element makes the entire card clickable without any JavaScript click handlers. The browser automatically associates the click with the hidden radio input. Make sure each radio input has a unique id if you also use explicit htmlFor labels.

How is a radio group different from a select dropdown?

Radio buttons show all options simultaneously, making comparison easy. A select dropdown hides all options until opened, saving space when there are many choices. Use radio buttons when there are 2–5 options and users benefit from seeing them all at once. Use a dropdown for 6+ options or when screen space is limited.

Related topics

Build this form visually — no code needed

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