React Form with Survey Form
An NPS-style survey combines a numeric rating picker, free-text feedback, and a feature usage checkbox group into a single cohesive form. The key UX moment is the thank-you screen after submission that reflects the user's score back to them — this makes the submission feel acknowledged and personalised rather than generic.
The challenge
Survey forms require multiple distinct input types — rating scales, checkboxes, and textareas — all working together with a thank-you state after submission.
- Displaying the NPS scale (1–10) as individual clickable buttons that highlight the selected score
- Combining three different question types into a single submit action with one success state
- Showing the submitted score on the thank-you screen requires keeping it in state
- Making the button grid for ratings look good on both mobile and desktop
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.
import { useState } from 'react';
const FEATURES = ['Form Builder', 'Response Exports', 'Email Notifications', 'Conditional Logic', 'API Access'];
export default function SurveyForm() {
const [score, setScore] = useState(null);
const [feedback, setFeedback] = useState('');
const [features, setFeatures] = useState([]);
const [submitted, setSubmitted] = useState(false);
const toggleFeature = (f) =>
setFeatures((prev) => prev.includes(f) ? prev.filter((x) => x !== f) : [...prev, f]);
const handleSubmit = (e) => {
e.preventDefault();
if (score === null) { alert('Please select a score'); return; }
setSubmitted(true);
};
if (submitted)
return (
<div className="max-w-sm mx-auto p-6 bg-white rounded-2xl shadow space-y-3 text-center">
<div className={`text-5xl font-black ${score >= 9 ? 'text-green-500' : score >= 7 ? 'text-yellow-500' : 'text-red-500'}`}>
{score}
</div>
<p className="font-semibold text-gray-800">Thank you for your feedback!</p>
<p className="text-sm text-gray-500">
{score >= 9 ? "We're thrilled you love it." : score >= 7 ? 'Good to know — we're always improving.' : 'We hear you. Your input helps us get better.'}
</p>
{features.length > 0 && (
<p className="text-xs text-gray-400">Features you use: {features.join(', ')}</p>
)}
</div>
);
return (
<form onSubmit={handleSubmit} className="max-w-sm mx-auto p-6 bg-white rounded-2xl shadow space-y-6">
<h2 className="text-xl font-bold text-gray-800">Quick Survey</h2>
<div>
<p className="text-sm font-medium text-gray-700 mb-3">
How likely are you to recommend us? <span className="text-red-400">*</span>
</p>
<div className="flex gap-1 flex-wrap">
{Array.from({ length: 10 }, (_, i) => i + 1).map((n) => (
<button key={n} type="button" onClick={() => setScore(n)}
className={`w-9 h-9 rounded-lg text-sm font-semibold border transition ${
score === n
? 'bg-teal-600 border-teal-600 text-white'
: 'border-gray-300 text-gray-600 hover:border-teal-400 hover:text-teal-600'
}`}>
{n}
</button>
))}
</div>
<div className="flex justify-between text-xs text-gray-400 mt-1 px-0.5">
<span>Not likely</span><span>Very likely</span>
</div>
</div>
<div>
<p className="text-sm font-medium text-gray-700 mb-2">What could be better?</p>
<textarea value={feedback} onChange={(e) => setFeedback(e.target.value)}
rows={3} style={{ resize: 'none' }} placeholder="Share your thoughts…"
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-teal-500" />
</div>
<div>
<p className="text-sm font-medium text-gray-700 mb-2">Which features do you use?</p>
<div className="space-y-1.5">
{FEATURES.map((f) => (
<label key={f} className="flex items-center gap-2 cursor-pointer">
<input type="checkbox" checked={features.includes(f)} onChange={() => toggleFeature(f)}
className="w-4 h-4 accent-teal-600" />
<span className="text-sm text-gray-700">{f}</span>
</label>
))}
</div>
</div>
<button type="submit"
className="w-full bg-teal-600 text-white font-semibold py-2 rounded-lg hover:bg-teal-700 transition">
Submit Survey
</button>
</form>
);
}How ReactForm.co helps
ReactForm's visual builder handles all of the above — survey 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 visuallyFrequently asked questions
How do I build a 1-10 NPS rating picker in React?
Generate an array of numbers 1–10 with Array.from({ length: 10 }, (_, i) => i + 1). Map over it to render a button for each number. Store the selected score in state with useState(null). Apply conditional classes to highlight the selected button and clear the highlight on the others.
How do I show a personalised thank-you screen after a survey?
Keep the submitted values in state — do not clear them on submission. When submitted is true, render the thank-you screen and reference the stored values directly. For an NPS survey, you can show different messages based on the score: detractors (1–6), passives (7–8), and promoters (9–10).
How do I validate that a required survey question is answered?
Check the state value in your handleSubmit function before setting submitted to true. For the rating picker, check score !== null. For a checkbox group, check features.length > 0. Show an alert or an inline error message and return early if validation fails. The survey should not submit until required questions are answered.
Related topics
Build this form visually — no code needed
ReactForm.co handles survey form fields, validation, conditional logic, and responsive layout automatically. Publish in minutes and collect responses for free.

