Multi-Page Survey
A multi-step survey with progress tracking, back/next navigation, and per-page validation.
Schema
schema.tstsx
import type { SurveySchema } from 'react-minimal-survey-builder';
const schema: SurveySchema = {
id: 'employee-survey',
title: 'Employee Satisfaction Survey',
description: 'Help us improve your workplace experience.',
pages: [
{
id: 'personal',
title: 'Personal Information',
description: 'Tell us about yourself.',
questions: [
{
id: 'name',
type: 'text',
label: 'Full name',
required: true,
placeholder: 'John Doe',
},
{
id: 'department',
type: 'select',
label: 'Department',
required: true,
options: [
{ label: 'Engineering', value: 'eng' },
{ label: 'Design', value: 'design' },
{ label: 'Marketing', value: 'marketing' },
{ label: 'Sales', value: 'sales' },
{ label: 'HR', value: 'hr' },
{ label: 'Other', value: 'other' },
],
},
{
id: 'tenure',
type: 'radio',
label: 'How long have you been with the company?',
required: true,
options: [
{ label: 'Less than 1 year', value: '<1' },
{ label: '1–3 years', value: '1-3' },
{ label: '3–5 years', value: '3-5' },
{ label: '5+ years', value: '5+' },
],
},
],
},
{
id: 'satisfaction',
title: 'Satisfaction',
description: 'Rate your experience.',
questions: [
{
id: 'overall-rating',
type: 'rating',
label: 'Overall satisfaction',
required: true,
},
{
id: 'work-life',
type: 'rating',
label: 'Work-life balance',
required: true,
},
{
id: 'management',
type: 'rating',
label: 'Quality of management',
},
],
},
{
id: 'feedback',
title: 'Open Feedback',
description: 'Share your thoughts.',
questions: [
{
id: 'improvements',
type: 'checkbox',
label: 'What areas need improvement?',
options: [
{ label: 'Communication', value: 'communication' },
{ label: 'Tools & Technology', value: 'tools' },
{ label: 'Career Growth', value: 'growth' },
{ label: 'Compensation', value: 'compensation' },
{ label: 'Office Environment', value: 'environment' },
],
},
{
id: 'best-thing',
type: 'textarea',
label: 'What do you like most about working here?',
placeholder: 'Tell us what we do well...',
},
{
id: 'suggestions',
type: 'textarea',
label: 'Any suggestions for improvement?',
placeholder: 'Your ideas matter...',
},
],
},
],
settings: {
showProgress: true,
allowBack: true,
validateOnPageChange: true,
submitText: 'Submit Survey',
nextText: 'Continue',
prevText: 'Go Back',
},
};Using SurveyRenderer
SurveyRenderer approachtsx
import { SurveyRenderer } from 'react-minimal-survey-builder';
function EmployeeSurvey() {
return (
<SurveyRenderer
schema={schema}
className="max-w-2xl mx-auto"
options={{
onSubmit: async (answers) => {
await fetch('/api/survey-responses', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(answers),
});
},
onPageChange: (pageIndex) => {
console.log('Navigated to page:', pageIndex);
},
}}
renderComplete={() => (
<div className="text-center py-12">
<h2 className="text-2xl font-bold mb-2">Thank you!</h2>
<p className="text-gray-500">Your feedback has been recorded.</p>
</div>
)}
/>
);
}Using useSurvey Hook
useSurvey approachtsx
import { useSurvey } from 'react-minimal-survey-builder';
function EmployeeSurveyCustom() {
const {
survey, answers, setAnswer,
getVisibleQuestions, getError,
currentPageIndex, totalPages,
nextPage, prevPage, submit,
progress, isSubmitted, hasNextPage, hasPrevPage,
} = useSurvey(schema, {
onSubmit: async (answers) => {
await fetch('/api/survey-responses', {
method: 'POST',
body: JSON.stringify(answers),
});
},
});
if (isSubmitted) {
return <p className="text-center text-xl">Thank you for your feedback!</p>;
}
const currentPage = survey.pages[currentPageIndex];
return (
<div className="max-w-2xl mx-auto">
{/* Progress */}
<div className="mb-6">
<div className="flex justify-between text-sm text-gray-500 mb-1">
<span>Page {currentPageIndex + 1} of {totalPages}</span>
<span>{Math.round(progress)}%</span>
</div>
<div className="h-2 bg-gray-200 rounded-full">
<div
className="h-full bg-blue-500 rounded-full transition-all"
style={{ width: `${progress}%` }}
/>
</div>
</div>
{/* Page title */}
<h2 className="text-xl font-bold mb-1">{currentPage.title}</h2>
<p className="text-gray-500 mb-6">{currentPage.description}</p>
{/* Questions */}
{getVisibleQuestions().map((q) => (
<div key={q.id} className="mb-4">
<label className="block font-medium mb-1">
{q.label} {q.required && <span className="text-red-500">*</span>}
</label>
<input
value={(answers[q.id] as string) ?? ''}
onChange={(e) => setAnswer(q.id, e.target.value)}
className="w-full border rounded px-3 py-2"
placeholder={q.placeholder}
/>
{getError(q.id) && (
<p className="text-red-500 text-sm mt-1">{getError(q.id)}</p>
)}
</div>
))}
{/* Navigation */}
<div className="flex justify-between mt-8">
{hasPrevPage ? (
<button onClick={prevPage} className="px-4 py-2 border rounded">
Go Back
</button>
) : <div />}
{hasNextPage ? (
<button onClick={nextPage} className="px-4 py-2 bg-blue-500 text-white rounded">
Continue
</button>
) : (
<button onClick={submit} className="px-4 py-2 bg-green-500 text-white rounded">
Submit Survey
</button>
)}
</div>
</div>
);
}Key Features
- - Progress bar: Uses the
progressvalue (0–100) fromuseSurvey. - - Per-page validation: Prevents navigating forward with invalid answers when
validateOnPageChangeis true. - - Back navigation: Enabled via
allowBack: truein settings. - - Custom button text: Set via
submitText,nextText,prevTextin settings.