Overview
rquiz provides three types of interactive quizzes as HTML widgets:
| Function | Description |
|---|---|
singleQuestion() |
A single question with instant feedback (single- or multiple-choice) |
multiQuestions() |
A multi-question quiz with navigation, timer, and results |
fillBlanks() |
A fill-in-the-blank cloze text |
All quizzes work in R Markdown, Quarto documents, HTML presentations (e.g. revealJS), and Shiny applications.
Defining Questions
Single question (singleQuestion)
Questions are defined as a named list with 3 (or 4) elements:
myQuestionSC <- list(
question = "Which city hosted the 2024 Summer Olympics?", # the question text
options = c("Berlin", "London", "Paris", "Madrid"), # answer options
answer = 3, # index of the correct answer (Paris)
tip = "Think about the Eiffel Tower." # optional tip text
)For single-choice, answer is a single
integer. For multiple-choice, answer is a
vector of integers. The quiz mode is auto-detected from the length of
answer:
myQuestionMC <- list(
question = "Which of the following are prime numbers?",
options = c("2", "4", "7", "9", "11", "15"),
answer = c(1, 3, 5), # indices of correct options (2, 7, 11)
tip = "Prime numbers are only divisible by 1 and themselves."
)You can validate your question with
checkSingleQuestion() before passing it to the quiz
function. This catches common mistakes like missing fields, out-of-range
indices, or misspelled element names:
checkSingleQuestion(myQuestionSC) # returns invisibly if valid
# Common mistake: unnamed or partially named list
bad <- list("What is 2+2?", c("3", "4"), answer = 2)
checkSingleQuestion(bad)
#> Error: The question list must have named elements: 'question', 'options',
#> and 'answer' (plus optionally 'tip'). Missing: question, options.
#> Example: list(question = "What is 2+2?", options = c("3", "4", "5"),
#> answer = 2)
#> See ?checkSingleQuestion or ?singleQuestion for examples.Multiple questions (multiQuestions)
Pass a list of question lists. Each question follows the same
structure. Sub-lists can be named (e.g. q1,
q2) for readability, but this is not required.
Single-choice only - all questions have a single correct answer:
sportQuiz <- list(
q1 = list(
question = "Which city hosted the 2024 Summer Olympics?",
options = c("Athens", "London", "Paris", "Tokyo"),
answer = 3
),
q2 = list(
question = "Which sport was added to the Olympics at the 2024 Paris Games?",
options = c("Skateboarding", "Breakdance", "Surfing", "Sport Climbing"),
answer = 2
),
q3 = list(
question = "At the 1900 Olympics in Paris, one sport was featured that has never returned to the Games since. Which one?",
options = c("Live pigeon shooting", "Underwater archery", "Synchronized sneezing",
"Backwards horse vaulting"),
answer = 1
)
)Mixed with tips - some questions include a custom tip text:
sportQuizWithTips <- list(
q1 = list(
question = "Which city hosted the 2024 Summer Olympics?",
options = c("Athens", "London", "Paris", "Tokyo"),
answer = 3,
tip = "They speak French there."
),
q2 = list(
question = "Which sport was added to the Olympics at the 2024 Paris Games?",
options = c("Skateboarding", "Breakdance", "Surfing", "Sport Climbing"),
answer = 2
# no tip for this question - the tip button won't appear here
)
)Multiple-choice - if any question
has multiple correct answers (length(answer) > 1), all
questions automatically use checkboxes:
scienceQuiz <- list(
q1 = list(
question = "Which of these are noble gases?",
options = c("Helium", "Oxygen", "Neon", "Iron"),
answer = c(1, 3)
),
q2 = list(
question = "Which planet is closest to the Sun?",
options = c("Venus", "Mercury", "Mars"),
answer = 2 # single correct answer, but rendered as checkbox
)
)Validate the entire question list with
checkMultiQuestions():
checkMultiQuestions(sportQuiz) # returns invisibly if valid
# Common mistake: flat list instead of list of lists
bad <- list(question = "Q1?", options = c("A", "B"), answer = 1,
question = "Q2?", options = c("C", "D"), answer = 2)
checkMultiQuestions(bad)
#> Error: `x` looks like a single question, not a list of questions.
#> Wrap it in an outer list: list(q1 = list(question = ..., options = ...,
#> answer = ...))
#> See ?checkMultiQuestions or ?multiQuestions for examples.Fill-in-the-blank (fillBlanks)
Define a cloze text with $$!answer!$$ markers for blank
fields. Optionally add distractor options:
rQuiz <- list(
cloze = "R is a $$!programming!$$ language for $$!statistical computing!$$.",
addOptions = c("natural", "colloquial", "movies")
)Normal text wraps automatically across lines. For code formatting,
use <pre> tags — but note that inside
<pre>, you need <br> tags for
explicit line breaks:
codeQuiz <- list(
cloze = "<pre>x $$!<-!$$ c(1, 2, 3)
$$!mean!$$(x)</pre>",
addOptions = c("=", "->", "median", "sum")
)Validate with checkFillBlanks():
checkFillBlanks(rQuiz) # returns invisibly if valid
# Common mistake: misspelled element name
bad <- list(clozed = "R is a $$!programming!$$ language.")
checkFillBlanks(bad)
#> Error: Unknown element(s) in `x`: 'clozed'.
#> Allowed elements are: 'cloze' (required) and 'addOptions' (optional).
#> See ?checkFillBlanks or ?fillBlanks for examples.Creating a Quiz
Once your data is defined, pass it to the quiz function:
myQuestionSC <- list(
question = "Which city hosted the 2024 Summer Olympics?", # the question text
options = c("Berlin", "London", "Paris", "Madrid"), # answer options
answer = 3, # index of the correct answer (Paris)
tip = "Think about the Eiffel Tower." # optional tip text
)
singleQuestion(
x = myQuestionSC,
title = "Geography Quiz",
showTipButton = TRUE
)Key Parameters
All three quiz functions share a common set of parameters:
Content & behavior
| Parameter | Description | Default |
|---|---|---|
title |
Quiz title text |
NULL / "Quiz"
|
language |
Language for UI text ("en",
"de", "fr", "es") |
"en" |
shuffle |
Randomize option/question order | FALSE |
showTipButton |
Show a hint button | FALSE |
showSolutionButton |
Show a reveal-solution button | TRUE |
Tips: Set showTipButton = TRUE to
display a tip button. The behavior depends on the quiz type and
mode:
-
Single-choice (
singleQuestionandmultiQuestions): Tips are only shown if you provide a custom tip text as the 4th element of the question list (tip = "..."). Questions without a tip simply don’t show the button. - Multiple-choice: If no custom tip is provided, an automatic tip is generated showing the number of correct answers (e.g. “Number of correct answers: 3”). You can still override this with your own tip text.
-
Fill-in-the-blank (
fillBlanks): The tip shows all available answer options (correct answers + distractors fromaddOptions).
Submit & retry (MC mode in
singleQuestion): After clicking Submit, a feedback
message shows how many correct and wrong selections were made. The
Submit button is disabled until the user changes their selection (adds
or removes an option), then it can be clicked again. This allows
iterative learning — adjust your answer and retry without reloading the
page.
Shuffling: Set shuffle = TRUE to
randomize order:
-
singleQuestion: The answer options are displayed in a random order each time the widget is rendered (e.g. when the page is loaded or refreshed). -
multiQuestions: The question order is randomized on each render, and also each time the user clicks “Try again” on the results page.
Layout
| Parameter | Description | Default |
|---|---|---|
width |
Widget width (CSS value) | "100%" |
height |
Widget height (CSS value) | "500px" |
scroll |
Fixed height with scrollbar (useful for slides) | FALSE |
center |
Center the widget horizontally | TRUE |
Design
All visual aspects are customizable — colors, fonts, sizes. Common parameters include:
| Parameter | Description | Default |
|---|---|---|
fontFamily |
Font stack | "'Helvetica Neue', ..." |
fontSize |
Base font size in px | 16 |
titleCol / titleBg
|
Title text & background color |
"#FFF" / "#5F5F5F"
|
questionCol / questionBg
|
Question (or description in fillBlanks) text & background color. Also serves as feedback area (green/red). | varies |
See the function documentation (?singleQuestion,
?multiQuestions, ?fillBlanks) for the full
list of design parameters.
Reusable Themes
If you use multiple quizzes in the same document and want a
consistent look, create a reusable theme with rquizTheme()
instead of repeating color settings:
# Define a dark theme once
dark <- rquizTheme(
fontFamily = "Georgia, serif",
fontSize = 18,
titleCol = "#E0E0E0", titleBg = "#1A1A2E",
questionCol = "#FFFFFF", questionBg = "#16213E",
mainCol = "#E0E0E0", mainBg = "#1A1B2E",
optionBg = "#252540",
navButtonCol = "#FFFFFF", navButtonBg = "#E94560",
tipButtonCol = "#E0E0E0", tipButtonBg = "#2C2C3E",
solutionButtonCol = "#E0E0E0", solutionButtonBg = "#2C2C3E"
)
# Apply to any quiz - all share the same design:
singleQuestion(x = myQuestionSC, theme = dark, title = "Quiz 1")
multiQuestions(x = scienceQuiz, theme = dark, title = "Quiz 2")
fillBlanks(x = rQuiz, theme = dark, title = "Quiz 3")The theme provides defaults that are overridden by any explicitly passed arguments:
# Use the dark theme but override the title background:
singleQuestion(x = myQuestionSC, theme = dark, title = "Quiz 1",
titleBg = "#FF0000")See ?rquizTheme for all available theme parameters,
including quiz-specific ones like optionLabelBg
(singleQuestion), navButtonBg (multiQuestions), or
descriptFontSize (fillBlanks).
Formatting Text with HTML
Since quiz content is rendered as HTML, you can use HTML tags to
format text within questions, options, tips, and cloze text. Standard
Markdown formatting (*italic*, **bold**) does
not work inside quiz strings — use HTML tags
instead.
Code formatting in fill-in-the-blank quizzes
Use <pre> tags for monospace code blocks in
fillBlanks(). Inside <pre>, line breaks
require the <br> tag (normal line breaks are
ignored):
Multilingual Support
Set language to change all UI text (buttons, messages)
automatically:
# German
singleQuestion(x = myQuestionSC, language = "de")
# French
multiQuestions(x = sportQuiz, language = "fr")
# Spanish
fillBlanks(x = rQuiz, language = "es")Currently supported: English ("en"), German
("de"), French ("fr"), Spanish
("es"). To request additional languages, please open an
issue on GitHub.
Using in Quarto / R Markdown
Simply include quiz code in a code chunk. The widget renders as part of the HTML output:
```{r}
library(rquiz)
singleQuestion(
x = list(
question = "What is 2+2?",
options = c("3", "4", "5"),
answer = 2
),
title = "Math"
)
```For HTML slide presentations (e.g. revealJS,
xaringan), use scroll = TRUE to add a scrollbar when the
quiz exceeds the available slide space:
singleQuestion(x = myQuestionSC, scroll = TRUE, height = "400px")Using in Shiny
Use the provided output/render functions:
library(shiny)
library(rquiz)
ui <- fluidPage(
h2("My Quiz App"),
singleQuestionOutput("quiz")
)
server <- function(input, output) {
output$quiz <- renderSingleQuestion({
singleQuestion(
x = list(
question = "What is 2+2?",
options = c("3", "4", "5"),
answer = 2
)
)
})
}
shinyApp(ui, server)Each quiz type has its own pair: singleQuestionOutput()
/ renderSingleQuestion(),
multiQuestionsOutput() /
renderMultiQuestions(), fillBlanksOutput() /
renderFillBlanks().
Next Steps
- Browse the Gallery for interactive examples with different configurations
- Check the Function Reference for all available parameters
