useSurvey Hook
The useSurvey hook is the heart of the library. It gives you full, headless control over survey state, navigation, validation, and submission — while you own the rendering.
Signature
function useSurvey(
schema: SurveySchema,
options?: SurveyOptions
): UseSurveyReturn;Options
interface SurveyOptions {
initialAnswers?: SurveyAnswers;
onChange?: (answers: SurveyAnswers, questionId: string) => void;
onSubmit?: (answers: SurveyAnswers) => void | Promise<void>;
onValidate?: (errors: ValidationError[]) => void;
onPageChange?: (pageIndex: number) => void;
validators?: Record<string, (value: AnswerValue, question: Question, answers: SurveyAnswers) => string | null>;
}initialAnswersPre-populate answers. Useful for edit mode or saved progress.
onChangeCalled whenever an answer changes. Receives all answers and the changed question ID.
onSubmitCalled when the survey is submitted after validation passes. Can be async.
onValidateCalled when validation runs. Receives all validation errors.
onPageChangeCalled when navigating to a different page.
validatorsCustom validator functions keyed by question ID.
Return Object
interface UseSurveyReturn {
// Survey data
survey: SurveySchema; // Parsed schema
answers: SurveyAnswers; // Current answers
// Answer management
setAnswer: (id: string, value: AnswerValue) => void;
setAnswers: (answers: SurveyAnswers) => void;
// Validation
errors: ValidationError[]; // Current errors
getError: (id: string) => string | undefined;
isValid: boolean;
validate: () => boolean;
validateCurrentPage: () => boolean;
// Visibility
getVisibleQuestions: () => Question[];
getPageQuestions: (pageIndex: number) => Question[];
visiblePages: Page[];
// Navigation
currentPageIndex: number;
totalPages: number;
hasNextPage: boolean;
hasPrevPage: boolean;
nextPage: () => boolean;
prevPage: () => void;
goToPage: (index: number) => boolean;
// Submission
submit: () => Promise<void>;
isSubmitted: boolean;
reset: () => void;
// Progress
progress: number; // 0-100
}Basic Usage
import { useSurvey } from 'react-minimal-survey-builder';
function MyForm() {
const {
answers, setAnswer,
getVisibleQuestions, getError,
nextPage, prevPage, submit,
currentPageIndex, totalPages, progress,
} = useSurvey(schema, {
initialAnswers: { name: 'John' },
onSubmit: async (answers) => {
await saveToDatabase(answers);
},
});
return (
<form onSubmit={(e) => { e.preventDefault(); submit(); }}>
{/* Progress */}
<div className="progress" style={{ width: `${progress}%` }} />
{/* Questions */}
{getVisibleQuestions().map((q) => (
<div key={q.id}>
<label>{q.label}</label>
<input
value={(answers[q.id] as string) ?? ''}
onChange={(e) => setAnswer(q.id, e.target.value)}
/>
{getError(q.id) && <span className="error">{getError(q.id)}</span>}
</div>
))}
{/* Navigation */}
<div>
<span>Page {currentPageIndex + 1} of {totalPages}</span>
{currentPageIndex > 0 && <button onClick={prevPage}>Back</button>}
{currentPageIndex < totalPages - 1
? <button onClick={nextPage}>Next</button>
: <button type="submit">Submit</button>}
</div>
</form>
);
}Performance
Stable callbacks
All returned functions use refs internally so they maintain stable identity across re-renders. Safe to use as dependencies.
Memoized schema
The schema is parsed once and memoized. Subsequent renders skip the parsing step.
Computed visibility
Condition evaluation and visibility are recalculated only when answers change.