S
Documentation

SurveyRenderer

A drop-in component that renders a complete survey from a schema. It handles rendering, navigation, validation, and submission out-of-the-box with built-in WCAG 2.1 accessibility — while still giving you full control through render props and custom components.

Import

import { SurveyRenderer } from 'react-minimal-survey-builder';

Props

SurveyRendererPropstypescript
interface SurveyRendererProps {
  /** The survey JSON schema */
  schema: SurveySchema;
  /** Survey options (callbacks, initial answers, etc.) */
  options?: SurveyOptions;
  /** Custom component overrides by question type */
  components?: QuestionComponents;
  /** Custom class name for the survey container */
  className?: string;
  /** Render prop for full control over layout */
  children?: (survey: UseSurveyReturn) => React.ReactNode;
  /** Disable all inputs */
  disabled?: boolean;
  /** Custom header renderer */
  renderHeader?: (props: {
    title?: string;
    description?: string;
    progress: number;
    currentPage: number;
    totalPages: number;
  }) => React.ReactNode;
  /** Custom footer/navigation renderer */
  renderFooter?: (props: {
    hasPrevPage: boolean;
    hasNextPage: boolean;
    isLastPage: boolean;
    prevPage: () => void;
    nextPage: () => void;
    submit: () => void;
    isValid: boolean;
  }) => React.ReactNode;
  /** Custom success/thank-you screen */
  renderComplete?: () => React.ReactNode;
}
schema

The survey schema object that defines pages, questions, validation, and settings.

options

Callbacks and configuration: onSubmit, onChange, initialAnswers, validators, etc.

components

Map of custom React components keyed by question type. Overrides default rendering.

className

CSS class applied to the outermost survey container element.

children

Render prop that receives the full useSurvey return object for total layout control.

disabled

When true, all inputs are disabled. Useful for read-only or review modes.

renderHeader

Replaces the default header. Receives title, description, progress, current/total pages.

renderFooter

Replaces the default navigation footer. Receives navigation functions and state.

renderComplete

Custom completion/thank-you screen shown after submission.

Basic Usage

Basic Exampletsx
import { SurveyRenderer } from 'react-minimal-survey-builder';
import type { SurveySchema } from 'react-minimal-survey-builder';

const schema: SurveySchema = {
  id: 'demo',
  pages: [
    {
      id: 'page1',
      questions: [
        { id: 'name', type: 'text', label: 'Your name', required: true },
        { id: 'email', type: 'email', label: 'Email', required: true },
      ],
    },
  ],
};

function App() {
  return (
    <SurveyRenderer
      schema={schema}
      options={{
        onSubmit: async (answers) => {
          console.log('Submitted:', answers);
        },
      }}
    />
  );
}

Custom Components

Override the rendering of any question type by passing custom components:

Custom Componentstsx
import { SurveyRenderer } from 'react-minimal-survey-builder';
import type { QuestionComponentProps } from 'react-minimal-survey-builder';

function CustomTextInput({ question, value, onChange, error }: QuestionComponentProps) {
  return (
    <div className="my-custom-field">
      <label className="my-label">{question.label}</label>
      <input
        className="my-input"
        value={(value as string) ?? ''}
        onChange={(e) => onChange(e.target.value)}
        placeholder={question.placeholder}
      />
      {error && <p className="my-error">{error}</p>}
    </div>
  );
}

<SurveyRenderer
  schema={schema}
  components={{
    text: CustomTextInput,
    email: CustomTextInput,
  }}
  options={{ onSubmit: (answers) => console.log(answers) }}
/>

Render Prop

For full control over layout, use the children render prop. It receives the complete useSurvey return object:

Render Proptsx
<SurveyRenderer schema={schema} options={{ onSubmit: handleSubmit }}>
  {(survey) => (
    <div className="my-survey-layout">
      <h1>{survey.survey.title}</h1>
      <p>Progress: {survey.progress}%</p>

      {survey.getVisibleQuestions().map((q) => (
        <div key={q.id}>
          <label>{q.label}</label>
          <input
            value={(survey.answers[q.id] as string) ?? ''}
            onChange={(e) => survey.setAnswer(q.id, e.target.value)}
          />
          {survey.getError(q.id) && <span>{survey.getError(q.id)}</span>}
        </div>
      ))}

      <button onClick={survey.submit}>Submit</button>
    </div>
  )}
</SurveyRenderer>

Custom Header & Footer

Custom Header, Footer & Complete Screentsx
<SurveyRenderer
  schema={schema}
  options={{ onSubmit: handleSubmit }}
  renderHeader={({ title, progress, currentPage, totalPages }) => (
    <div className="my-header">
      <h2>{title}</h2>
      <div className="progress-bar" style={{ width: `${progress}%` }} />
      <span>Page {currentPage + 1} of {totalPages}</span>
    </div>
  )}
  renderFooter={({ hasPrevPage, isLastPage, prevPage, nextPage, submit }) => (
    <div className="my-footer">
      {hasPrevPage && <button onClick={prevPage}>Back</button>}
      {isLastPage
        ? <button onClick={submit}>Send</button>
        : <button onClick={nextPage}>Continue</button>}
    </div>
  )}
  renderComplete={() => (
    <div className="thank-you">
      <h2>🎉 Thank you!</h2>
      <p>Your response has been recorded.</p>
    </div>
  )}
/>

Disabled Mode

Pass disabled to render the survey in read-only mode. All inputs will be disabled:

Disabled/Read-onlytsx
<SurveyRenderer
  schema={schema}
  options={{ initialAnswers: savedAnswers }}
  disabled
/>

Accessibility

All built-in question components are fully WCAG 2.1 AA compliant out of the box. No extra configuration needed.

aria-required & required

Native required attributes on all form inputs so screen readers announce mandatory fields.

aria-invalid & aria-describedby

Invalid fields are announced with linked error messages for assistive technologies.

Proper ARIA roles

Radio groups use role="radiogroup", checkbox groups use role="group", rating uses role="radiogroup" with aria-checked.

aria-labelledby

All grouped inputs (radio, checkbox, boolean, rating) are linked to their visible label via aria-labelledby.

Keyboard focus-visible

Scoped :focus-visible outlines for all interactive elements — inputs, buttons, radios, checkboxes, and sliders.

aria-live on errors

Validation errors use role="alert" and aria-live="assertive" so they are announced immediately by screen readers.

aria-valuemin/max/now/text

Sliders expose their current value, range, and unit text to assistive technologies.

Descriptive aria-labels

Date, time, datetime, phone, and slider inputs include descriptive aria-label text for context.

Tip

If you need even more control, skip SurveyRenderer entirely and use the useSurvey hook directly. The renderer is built on top of the same hook.