# 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.

## How it is stored

- 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.

:::tip[Give important quizzes an explicit id]
If you might edit a quiz's title later, set an explicit `id` so saved progress is not lost when the title (and therefore
the fallback hash) changes.

```mdx
<Quiz id="chapter-1-intro">
  …
</Quiz>
```

:::

## Try it

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

<Quiz id="progress-demo">
Will this answer still be here after a reload?

- [x] Yes
- [ ] No

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

</Quiz>

```mdx title="quiz.mdx"
<Quiz>
Will this answer still be here after a reload?

- [x] Yes
- [ ] No

It is saved to local storage and restored when the page loads.
</Quiz>
```

## Aggregate progress

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`](/starlight-quiz/guides/configuration/#plugin-options) plugin option. To place it somewhere specific
(or to use it [without Starlight](/starlight-quiz/guides/vanilla-astro/)), import and drop in the
[`<QuizProgress />`](/starlight-quiz/guides/configuration/#quizprogress-props) component yourself.

A [`<QuizResults>`](/starlight-quiz/guides/results-screen/) 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.

## Build your own progress UI

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:

```js
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.