Standalone hook
Build a custom quiz interface around a standalone useQuiz session. Try starting the quiz, selecting answers, and viewing the final score, then explore the runnable source and quiz data behind the preview.
Try the quiz
Answer the questions and review your results.
Implementation
useQuiz provides quiz state and actions without rendering or styling an interface. This example builds a custom UI. For a complete styled quiz with no custom CSS, use the default Quiz component.
Install react-quiz-component, then copy this component and the quiz.ts data file into the same directory. The preview mounts in the browser; Next.js applications should use a client boundary.
'use client';
import './examples.css';
import { useQuiz } from 'react-quiz-component';
import { quiz } from './quiz';
export default function HookExample() {
const { state, actions } = useQuiz({ quiz });
return (
<section className="headless-demo" aria-label="Headless quiz">
{state.status === 'invalid' && (
<p role="alert">{state.errors.join(' ')}</p>
)}
{state.status === 'idle' && (
<>
<h2>{quiz.quizTitle}</h2>
<p>{quiz.quizSynopsis}</p>
<button onClick={actions.start}>Start quiz</button>
</>
)}
{state.status === 'active' && (
<>
<p>
Question {state.currentQuestionIndex + 1} of{' '}
{state.questions.length}
</p>
<h2>{state.currentQuestion?.question}</h2>
<div className="answers">
{state.answers.map((answer) => (
<button
key={answer.answerNumber}
disabled={answer.disabled}
aria-pressed={answer.selected}
onClick={() => actions.selectAnswer(answer.answerNumber)}
>
{answer.value}
</button>
))}
</div>
<p aria-live="polite">
{state.feedback === 'correct'
? 'Correct!'
: state.feedback === 'incorrect'
? 'Incorrect.'
: ''}
</p>
{state.canGoNext && (
<button onClick={actions.nextQuestion}>Next question</button>
)}
{state.canSubmit && (
<button onClick={actions.submitQuiz}>Finish quiz</button>
)}
</>
)}
{state.status === 'completed' && (
<output>
Score: {state.summary.correctPoints} / {state.summary.totalPoints}
</output>
)}
</section>
);
}
Custom styles
Save this stylesheet as examples.css alongside the component. The implementation imports it to style the layout, answer buttons, selected answers, and keyboard focus. The CSS uses the documentation theme colors when available, with fallback colors for your own application.
.headless-demo {
display: grid;
gap: 16px;
}
.headless-demo h2 {
font-size: 1.2rem;
font-weight: 600;
}
.headless-demo .answers {
display: grid;
gap: 8px;
}
.headless-demo .question-group {
display: grid;
min-inline-size: 0;
gap: 8px;
padding: 16px;
border: 1px solid var(--color-fd-border, #d4d4d8);
border-radius: 8px;
}
.headless-demo .question-group legend {
max-inline-size: 100%;
padding-inline: 4px;
font-weight: 600;
}
.headless-demo button,
.headless-demo select {
border: 1px solid var(--color-fd-border, #d4d4d8);
border-radius: 8px;
padding: 8px 12px;
margin: 4px 8px 4px 0;
cursor: pointer;
background: var(--color-fd-background, #fff);
color: var(--color-fd-foreground, #18181b);
}
.headless-demo button:hover {
background: var(--color-fd-muted, #f4f4f5);
}
.headless-demo button[aria-pressed='true'] {
border-color: var(--color-fd-primary, #2563eb);
background: var(--color-fd-accent, #eff6ff);
}
.headless-demo button:disabled {
opacity: 0.65;
cursor: default;
}
.headless-demo :focus-visible {
outline: 2px solid var(--color-fd-primary, #2563eb);
outline-offset: 3px;
}
Quiz data
import type { Quiz } from 'react-quiz-component';
export const quiz: Quiz = {
quizTitle: 'React essentials',
quizSynopsis: 'Two questions about React hooks and rendering.',
questions: [
{
question: 'Which hook stores local state?',
questionType: 'text',
answerSelectionType: 'single',
answers: ['useState', 'useEffect', 'useRef'],
correctAnswer: '1',
point: 10,
messageForCorrectAnswer: 'Correct! useState stores component state.',
messageForIncorrectAnswer: 'useState is the hook for local state.',
explanation: 'useState returns the current value and a setter.',
},
{
question: 'Which APIs render React content? Pick two.',
questionType: 'text',
answerSelectionType: 'multiple',
answers: ['createRoot', 'renderToString', 'querySelector'],
correctAnswer: [1, 2],
point: 20,
explanation:
'createRoot renders in the browser; renderToString renders HTML on the server.',
},
],
};
Custom results
Replace the default result page with your own score summary.
Provider and context
Share one quiz session across separate question, timer, navigation, and results components using QuizProvider and useQuizContext. Try pausing the timer, revisiting questions, and filtering results, then explore the complete implementation and quiz data.