Toggle Sidebar B
AppearanceLight & dark mode D

Questionnaire

A multi-step question form. One item is active at a time; choices, free text, skipping, keyboard shortcuts, and validation are handled for you. It renders a real form, so submission and autofill work without extra wiring.

Default

Two single-choice questions. Questionnaire renders a form, QuestionnaireItem is a fieldset, and each QuestionnaireChoice is a radio input.

Replay preview
Which stack do you reach for first?

Multiple choice

multiple turns the choices into checkboxes and keeps every selected answer.

Replay preview

Free text

QuestionnaireInput collects an open answer. Mix it with choices in the same item when you need an other option.

Replay preview

Optional questions

Leave required off and add QuestionnaireSkip so a reader can move on. A skipped item clears its answers.

Replay preview

Keyboard shortcuts

shortcuts assigns a key to every choice. Use letters for A, B, C or numbers for 1, 2, 3.

Replay preview

Controlled

Bind the active item with v-model:item to drive navigation from outside, or to persist where the reader stopped. Without it the questionnaire is uncontrolled and manages its own position.

Replay preview
Active item: theme
Which theme do you prefer?

Validation

invalid marks an item from outside, so any schema library can drive it. QuestionnaireError renders the message and is wired to aria-describedby. Set noValidate to false on the root if you also want native constraint validation on answered items.

Replay preview

Tracking status

Each item emits update:status with unanswered, answered, or skipped. Use it to gate a submit button, save a draft, or drive a step indicator outside the form.

Replay preview
unanswered

Conditional items

disabled removes an item from the flow without unmounting it, so an answer survives if the reader backtracks and changes an earlier choice. Progress and navigation both skip disabled items.

Replay preview

Custom progress

QuestionnaireProgress exposes current, total, first, and last through its default slot. Replace the built-in Question N of M label with a bar, dots, or anything else; the progressbar role and aria-live stay on the wrapper.

Replay preview

Declaring items up front

The items prop declares the item order and the choice order shortcuts are assigned in. Pass it when the questionnaire is server rendered, so progress reads the right count and shortcuts land on the right choices before hydration.

Replay preview
What is your role?

Progress reads 1 of 2 on the server too.

Question 1 of 2

In a card

The root is a plain form, so it drops into a Card, a Dialog, or a Drawer without changes. Give the wrapper the width and let the questionnaire fill it.

Replay preview

Quick check

Two taps and you are done.

Anatomy

Import all parts and piece them together.

vue
<script setup>
import {
  Questionnaire,
  QuestionnaireActions,
  QuestionnaireChoice,
  QuestionnaireChoiceDescription,
  QuestionnaireChoices,
  QuestionnaireDescription,
  QuestionnaireError,
  QuestionnaireInput,
  QuestionnaireItem,
  QuestionnaireNext,
  QuestionnairePrevious,
  QuestionnaireProgress,
  QuestionnaireSkip,
  QuestionnaireSubmit,
  QuestionnaireTitle,
} from "@/components/ui/questionnaire";
</script>

<template>
  <Questionnaire>
    <QuestionnaireItem>
      <QuestionnaireTitle />
      <QuestionnaireDescription />
      <QuestionnaireChoices>
        <QuestionnaireChoice>
          <QuestionnaireChoiceDescription />
        </QuestionnaireChoice>
      </QuestionnaireChoices>
      <QuestionnaireInput />
      <QuestionnaireError />
    </QuestionnaireItem>
    <QuestionnaireActions>
      <QuestionnairePrevious />
      <QuestionnaireProgress />
      <QuestionnaireSkip />
      <QuestionnaireNext />
      <QuestionnaireSubmit />
    </QuestionnaireActions>
  </Questionnaire>
</template>

API Reference

Questionnaire

PropTypeDefaultDescription
itemstringControlled active item name. Use with v-model:item.
defaultItemstringItem shown first. Ignored when item is provided.
itemsQuestionnaireItemDefinition[]Declares the item order, and the choice order shortcuts are assigned in.
shortcuts"letters" | "numbers"Assigns a keyboard shortcut to every choice.
noValidatebooleantrueSet to false to run native constraint validation on answered items.
classstringExtra classes, merged with cn().
EventDescription
update:itemActive item changed.
submitThe form was submitted.
resetThe form was reset.
SlotDescription
defaultItems and actions.

QuestionnaireItem

PropTypeDefaultDescription
namestringRequired. Submitted under this name, and used to activate the item.
requiredbooleanfalseRequires an answer before the questionnaire can continue.
multiplebooleanfalseRenders choices as checkboxes and keeps every selected answer.
invalidbooleanfalseMarks the item invalid from outside, for example after schema validation.
disabledbooleanfalseExcludes the item from the questionnaire without unmounting it.
EventDescription
update:statusStatus changed: "unanswered", "answered", or "skipped".
SlotDescription
defaultTitle, description, choices, input, error.

QuestionnaireChoice

PropTypeDefaultDescription
valuestringRequired. Submitted as the answer of the parent item.
checkedbooleanundefinedControlled checked state. Use with v-model:checked.
defaultCheckedbooleanfalseChecks the choice on mount and after a native form reset.
disabledbooleanfalseBlocks selection and removes the choice from validation.
EventDescription
update:checkedChecked state changed.
changeThe underlying input fired change.
SlotDescription
defaultLabel text and QuestionnaireChoiceDescription.

QuestionnaireInput

PropTypeDefaultDescription
modelValuestring | numberControlled value. Use with v-model.
defaultValuestring | numberFills the answer on mount and after a native form reset.
type"text" | "email" | "number" | "tel" | "url" | "search" | "password" | "date" | "datetime-local" | "month" | "time" | "week""text"Native input type.
disabledbooleanfalseBlocks entry and removes the answer from validation.
placeholderstringForwarded to the native input, along with any other attribute.
classstringExtra classes, merged with cn().
EventDescription
update:modelValueValue changed.

QuestionnaireProgress

PropTypeDefaultDescription
asstring | Component"div"Element rendered by the reka-ui Primitive.
SlotDescription
defaultScoped: { current, total, first, last }. Falls back to Question N of M.

QuestionnaireNext

PropTypeDefaultDescription
variantButtonVariants['variant']"default"Button variant.
sizeButtonVariants['size']"default"Button size.
disabledbooleanfalseDisables the button on top of the automatic state.
EventDescription
clickCall preventDefault to stop navigation.

QuestionnairePrevious

PropTypeDefaultDescription
variantButtonVariants['variant']"ghost"Button variant.
sizeButtonVariants['size']"default"Button size.
EventDescription
clickCall preventDefault to stop navigation.

QuestionnaireSkip

PropTypeDefaultDescription
variantButtonVariants['variant']"ghost"Button variant.
EventDescription
clickCall preventDefault to stop skipping.

QuestionnaireSubmit

PropTypeDefaultDescription
variantButtonVariants['variant']"default"Button variant.
SlotDescription
defaultButton label.

QuestionnaireActions

SlotDescription
defaultPrevious, progress, skip, next, submit.

QuestionnaireTitle

SlotDescription
defaultThe question itself. Renders a legend.

QuestionnaireDescription

SlotDescription
defaultSupporting copy, wired to aria-describedby.

QuestionnaireChoiceDescription

SlotDescription
defaultSupporting copy under a choice label.

QuestionnaireError

SlotDescription
defaultValidation message for the active item.

QuestionnaireChoices

SlotDescription
defaultQuestionnaireChoice elements.

Accessibility

Keyboard shortcuts and ARIA behavior.

Shortcut Description
TabMoves through the choices and the action buttons.
SpaceToggles the focused choice.
Moves to the next answer in the active item. Single-choice items hand this back to the browser, so a radio group keeps its native roving focus.
Moves to the previous answer. Same handover as ArrowDown: the component only steps in for checkboxes and for text inputs mixed with choices.
Goes to the next question, once the current one is answered or skipped. Ignored inside a text input or while a radio has focus.
Goes to the previous question. Ignored inside a text input or while a radio has focus.
EnterConfirms the focused answer when it is filled, which advances or submits on the last item.
Ctrl + EnterConfirms from anywhere in the item, including from inside a text input.
AWith shortcuts="letters", focuses and selects the matching choice. Use 1, 2, 3 with shortcuts="numbers". Ignored while a text input has focus.
  • Each item renders a fieldset with its title as the legend, so a screen reader announces the question before the choices.
  • Descriptions and error messages are wired to the item through aria-describedby; an invalid item exposes aria-invalid.
  • QuestionnaireProgress is a named progressbar with aria-live, so the question count is announced on navigation.
  • Inactive items are marked hidden and inert, so they stay out of the tab order and out of the accessibility tree.
  • The buttons are native buttons and the answers are native inputs, so form submission, autofill, and native reset all keep working.
  • Assigned shortcuts are announced through aria-keyshortcuts on the choice and on the Next button.