S
Documentation

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 progress value (0–100) from useSurvey.
  • - Per-page validation: Prevents navigating forward with invalid answers when validateOnPageChange is true.
  • - Back navigation: Enabled via allowBack: true in settings.
  • - Custom button text: Set via submitText, nextText, prevText in settings.