3 unstable releases

0.2.0 Sep 21, 2023
0.1.1 Sep 20, 2023
0.1.0 Sep 20, 2023

#260 in Database interfaces

Download history 95/week @ 2023-09-19 30/week @ 2023-09-26 15/week @ 2023-10-03 12/week @ 2023-10-10 27/week @ 2023-10-17 22/week @ 2023-10-24 16/week @ 2023-10-31 12/week @ 2023-11-07 16/week @ 2023-11-14 21/week @ 2023-11-21 27/week @ 2023-11-28

78 downloads per month
Used in 2 crates


168 lines

mdbook-quiz: interactive quizzes for Markdown

tests crates.io

live demo

This repository provides an mdBook preprocessor that allows you to add interactive quizzes to your Markdown books. A quiz looks like this:

Screenshot of mdbook-quiz embedded in a web page

Table of contents:


These instructions assume you have an mdBook already set up. Unfamiliar with mdBook? Read the mdBook guide!

From crates.io

cargo install mdbook-quiz --locked

Note: this tool is under active development. I recommend pinning to a specific version to avoid breakage, e.g. by running

cargo install mdbook-quiz --locked --version <YOUR_VERSION>

And you can check your version by running mdbook-quiz -V. This repository uses semantic versioning for the quiz data format, so your quizzes should not break if you update to a more recent patch.

From source

You need Cargo, cargo-make, and Depot installed. Then run:

git clone https://github.com/cognitive-engineering-lab/mdbook-quiz
cd mdbook-quiz
cargo make init-bindings
cargo install --path crates/mdbook-quiz


First, create a quiz file. Quizzes are encoded as TOML files (see Quiz schema). For example:

# quizzes/rust-variables.toml
type = "ShortAnswer"
prompt.prompt = "What is the keyword for declaring a variable in Rust?"
answer.answer = "let"
context = "For example, you can write: `let x = 1`"

Then in your Markdown file, add a reference to the quiz file:

<!-- src/your-chapter.md -->

And now, a _quiz_:

{{#quiz ../quizzes/rust-variables.toml}}

Configure your book.toml to activate mdbook-quiz.

# book.toml

Then mdbook build should correctly embed the quiz.

Note: due to limitations of mdBook (see mdBook#1087), the mdbook-quiz preprocessor will copy files into your book's source directory under a subdirectory named mdbook-quiz. I recommend adding this directory to your .gitignore.

Quiz schema

A quiz is an array of questions.

export interface Quiz {
  questions: Question[];

A question is one of a set of predefined question types.

export type Question = ShortAnswer | Tracing | MultipleChoice;

Each question type is an instantiation of this Typescript interface:

export interface QuestionFields<Type extends string, Prompt, Answer> {
  type: Type;
  prompt: Prompt;
  answer: Answer;
  context?: Markdown;

It has a discriminating string name type and then a prompt and answer, along with additional context for explaining the answer.

Note that the Markdown type is just a string, but will be interpreted as Markdown by the quiz renderer.

Currently, mdbook-quiz supports these question types:

Short answer

A question where the answer is a one-line string.


type = "ShortAnswer"
prompt.prompt = "What is the keyword for declaring a variable in Rust?"
answer.answer = "let"
context = "For example, you can write: `let x = 1`"


export interface ShortAnswerPrompt {
  /** The text of the prompt. */
  prompt: Markdown;

export interface ShortAnswerAnswer {
  /** The exact string that answers the question. */
  answer: string;

  /** Other acceptable strings answers. */
  alternatives?: string[];

export type ShortAnswer = QuestionFields<"ShortAnswer", ShortAnswerPrompt, ShortAnswerAnswer>;

Multiple choice

A question with multiple options that the user selects from.


type = "MultipleChoice"
prompt.prompt = "What does it mean if a variable `x` is immutable?"
prompt.distractors = [
  "`x` is stored in the immutable region of memory.",
  "After being defined, `x` can be changed at most once.",
  "You cannot create a reference to `x`."
answer.answer = "`x` cannot be changed after being assigned to a value."
context = """
Immutable means "not mutable", or not changeable.


export interface MultipleChoicePrompt {
  /** The text of the prompt. */
  prompt: Markdown;

  /** An array of incorrect answers. */
  distractors: Markdown[];

  /** If defined, don't randomize distractors and put answer at this index. */
  answerIndex?: number;

export interface MultipleChoiceAnswer {
  /** The text of the correct answer. */
  answer: Markdown;


A question where the user has to predict how a program will execute (or fail to compile).


type = "Tracing"
prompt.program = """
fn main() {
  let x = 1;
  x += 1;
answer.doesCompile = false
context = """
This is a compiler error because line 4 tries to mutate `x` when `x` is not marked as `mut`.


export interface TracingPrompt {
  /** The contents of the program to trace */
  program: string;

export interface TracingAnswer {
  /** True if the program should pass the compiler */
  doesCompile: boolean;

  /** If doesCompile=true, then the contents of stdout after running the program */
  stdout?: string;  

export type Tracing = QuestionFields<"Tracing", TracingPrompt, TracingAnswer>;

Quiz configuration

You can configure mdbook-quiz by adding options to the [preprocessor.quiz] section of book.toml. The options are:

  • fullscreen (boolean): If true, then a quiz will take up the web page's full screen during use.
  • cache-answers (boolean): If true, then the user's answers will be saved in their browser's localStorage. Then the quiz will show the user's answers even after they reload the page.
  • more-words (path): An optional path to a .dic file that adds valid words to the spellchecker.


The schema for questions used in mdbook-quiz. Intended to be deserialized from a TOML file. See Quiz as the top-level type. Here is an example of a quiz:

id = "b230bed3-d6ba-4048-8b06-aa655d837b04"
type = "MultipleChoice"
prompt.prompt = "What is 1 + 1?"
prompt.distractors = ["1", "3", "**infinity**"]
answer.answer = ["2"]
context = """
Consult your local mathematician for details.

Note that all Rust identifiers with multiple words (e.g. does_compile) use camelCase keys, so should be written as doesCompile in the TOML.


~27K SLoC