Розробка квізу з розрахунком результату на сайті
Квіз — маркетинговий інструмент, який генерує лідів через залучення. Правильно побудований квіз збирає контакти, сегментує аудиторію та рекомендує продукт. Неправильний — просто набір запитань без цінності для користувача.
Типи квізів
Типологічний — кожна відповідь додає очки до одного або кількох «профілів». В кінці переможець визначається профілем з максимальною кількістю очок. Використовується для «Який вид клієнта ви є», «Давайте підберемо правильний тариф для вас».
Балльний — відповіді дають числові очки, остаточний результат визначається діапазоном. Типово для тестів знань, оцінки рівня.
Розгалужений (branching) — наступне запитання залежить від відповіді на попереднє. Дозволяє будувати складні сценарії з різними результатами.
Рекомендаційний — квіз збирає параметри та на виході рекомендує конкретний продукт, тариф, спеціаліста.
Структура даних
interface QuizQuestion {
id: string;
text: string;
type: 'single' | 'multiple' | 'scale';
answers: QuizAnswer[];
nextQuestion?: string | ((answers: Record<string, string[]>) => string); // для розгалуження
}
interface QuizAnswer {
id: string;
text: string;
scores: Record<string, number>; // { profile_a: 3, profile_b: 1 }
image?: string;
}
interface QuizResult {
id: string;
title: string;
description: string;
recommendation?: string;
cta?: { text: string; url: string };
minScore?: number; // для балльного типу
maxScore?: number;
}
Рушій розрахунку
class QuizEngine {
constructor(questions, results) {
this.questions = questions;
this.results = results;
this.answers = {}; // questionId -> answerId[]
this.scores = {};
}
answer(questionId, answerIds) {
this.answers[questionId] = answerIds;
const question = this.questions.find(q => q.id === questionId);
for (const answerId of answerIds) {
const answer = question.answers.find(a => a.id === answerId);
if (!answer?.scores) continue;
for (const [profile, score] of Object.entries(answer.scores)) {
this.scores[profile] = (this.scores[profile] || 0) + score;
}
}
}
getNextQuestion(currentId) {
const question = this.questions.find(q => q.id === currentId);
if (typeof question.nextQuestion === 'function') {
return question.nextQuestion(this.answers);
}
return question.nextQuestion;
}
calculateResult() {
// Типологічний: переможець за очками
const [topProfile] = Object.entries(this.scores)
.sort(([, a], [, b]) => b - a);
return this.results.find(r => r.id === topProfile?.[0]) ?? this.results[0];
}
getTotalScore() {
return Object.values(this.scores).reduce((s, v) => s + v, 0);
}
}
React-компонент квізу
function Quiz({ config }) {
const engine = useRef(new QuizEngine(config.questions, config.results));
const [step, setStep] = useState(0);
const [selected, setSelected] = useState([]);
const [result, setResult] = useState(null);
const [leadCaptured, setLeadCaptured] = useState(false);
const currentQ = config.questions[step];
const progress = ((step / config.questions.length) * 100).toFixed(0);
function handleNext() {
engine.current.answer(currentQ.id, selected);
setSelected([]);
if (step + 1 >= config.questions.length) {
setResult(engine.current.calculateResult());
} else {
setStep(s => s + 1);
}
}
if (result && !leadCaptured) {
return <LeadCapture onSubmit={(contact) => {
submitLead({ contact, result, answers: engine.current.answers });
setLeadCaptured(true);
}} />;
}
if (result && leadCaptured) {
return <QuizResult result={result} score={engine.current.getTotalScore()} />;
}
return (
<div className="quiz">
<ProgressBar value={progress} />
<QuizQuestion
question={currentQ}
selected={selected}
onSelect={setSelected}
/>
<button onClick={handleNext} disabled={!selected.length}>
{step + 1 < config.questions.length ? 'Далі' : 'Отримати результат'}
</button>
</div>
);
}
Захоплення лідів перед результатом
Класична механіка: користувач відповідає на всі запитання, але бачить результат тільки після введення email (або телефону). Конверсія вище, ніж у звичайної форми підписки, тому що людина вже витратила час.
function LeadCapture({ onSubmit }) {
const { register, handleSubmit } = useForm();
return (
<form onSubmit={handleSubmit(onSubmit)}>
<h3>Ваш результат готовий!</h3>
<p>Укажіть email, щоб отримати персональні рекомендації</p>
<input {...register('email', { required: true })} type="email" placeholder="[email protected]" />
<input {...register('name')} placeholder="Ім'я (необов'язково)" />
<button type="submit">Показати результат</button>
</form>
);
}
Аналітика
Для квізів важливо відстежувати не тільки фінальні конверсії, але й вибуток на кожному кроці:
// GTM / GA4
function trackStep(questionIndex, questionId) {
window.dataLayer?.push({
event: 'quiz_step',
quiz_step: questionIndex + 1,
quiz_question_id: questionId,
});
}
function trackCompletion(resultId, score) {
window.dataLayer?.push({
event: 'quiz_complete',
quiz_result: resultId,
quiz_score: score,
});
}
Анімація переходів
Переходи між запитаннями покращують враження від продукту. Проста анімація через CSS:
.quiz-question {
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from { opacity: 0; transform: translateX(24px); }
to { opacity: 1; transform: translateX(0); }
}
Для React — framer-motion з AnimatePresence для плавного видалення попереднього запитання.
Терміни
Простий балльний квіз на 5–10 запитань із захопленням лідів та результатом — 3–4 робочі дні. Розгалужений квіз з кількома профілями, анімацією, інтеграцією в CRM та A/B-тестуванням конфігурацій — 8–12 днів.







