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.
Multiple choice
multiple turns the choices into checkboxes and keeps every selected answer.
Free text
QuestionnaireInput collects an open answer. Mix it with choices in the same item when you need an other option.
Optional questions
Leave required off and add QuestionnaireSkip so a reader can move on. A skipped item clears its answers.
Keyboard shortcuts
shortcuts assigns a key to every choice. Use letters for A, B, C or numbers for 1, 2, 3.
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.
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.
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.
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.
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.
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.
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.
Quick check
Two taps and you are done.
Anatomy
Import all parts and piece them together.
<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
QuestionnaireItem
QuestionnaireChoice
QuestionnaireInput
QuestionnaireProgress
QuestionnaireNext
QuestionnairePrevious
QuestionnaireSkip
QuestionnaireSubmit
QuestionnaireActions
QuestionnaireTitle
QuestionnaireDescription
QuestionnaireChoiceDescription
QuestionnaireError
QuestionnaireChoices
Accessibility
Keyboard shortcuts and ARIA behavior.
- 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.