Skip to content

Progress tracking

Every quiz records whether it has been answered and whether the answer was correct. That progress is saved to the browser’s localStorage, so it survives reloads and revisits.

  • Progress is keyed by the page path plus each quiz’s id.
  • A quiz’s id comes from its id prop, falling back to a stable hash of its title. Because the key is stable rather than positional, progress survives reordering quizzes and rebuilding the site.
  • The whole page’s progress is stored under a single key, validated on load, and discarded if it is ever corrupted or unexpectedly large. A bad write can never break a page.

Answer this quiz, then reload the page, and your answer is restored automatically:

Will this answer still be here after a reload?

  • Yes
  • No

It is saved to local storage and restored when the page loads.

When used as a Starlight plugin, a compact progress widget appears in the table of contents (right sidebar on desktop, and the mobile ToC), showing how many quizzes on the page are answered and how many are correct. On narrow screens (where there is no right sidebar) a full-width version with a reset link is rendered at the foot of the page instead. The widget hides itself on pages without quizzes, and you can turn it off with the progressTracker plugin option. To place it somewhere specific (or to use it without Starlight), import and drop in the <QuizProgress /> component yourself.

A <QuizResults> panel shows the same progress in-page and lets the reader reset every quiz at once. Both read from a central tracker that also broadcasts window events, so you can build your own progress UI on top of it.

The tracker dispatches two events on window whenever progress changes:

Event Detail
starlight-quiz:progress { total, answered, correct, percentage, score } (counts plus 0–100 %)
starlight-quiz:reset-all (none), fired when every quiz on the page is reset

percentage is answered / total; score is correct / total. Both events are typed on WindowEventMap, so TypeScript knows event.detail in editors. Listen for them to drive a custom widget:

window.addEventListener('starlight-quiz:progress', (event) => {
const { answered, total, correct, percentage, score } = event.detail;
console.log(`Answered ${answered}/${total} (${percentage}%), score ${score}%`);
document.querySelector('#my-progress')?.style.setProperty('--done', String(percentage));
});
window.addEventListener('starlight-quiz:reset-all', () => {
console.log('All quizzes on this page were reset');
});

The progress event fires whenever progress changes, including as each quiz registers on load (restoring saved answers), so a listener gets the current totals without special-casing the initial render.