Questionnaire
A multi-step questionnaire with single-choice, multiple-choice, freeform, and skippable questions. Built on @shadcn/react, which handles focus management, keyboard navigation, and validation — this repo only supplies the styling.
Last updated August 17, 2026
Copied from shadcn/ui.
- Swapped Previous/Next chevron placement and rtl:-scale-x-100 mirroring to match this repo's Pagination convention (ChevronLeftIcon for Previous, ChevronRightIcon for Next, both flipped under rtl:)
- Replaced the upstream's icon-abstraction check/radio indicator with a plain lucide-react CheckIcon and a small filled dot, driven by this repo's group-data-* attribute styling convention
Overview
A single-choice question with a freeform "other" answer, followed by an optional multiple-choice question.
Installation
$ npx shadcn@latest add https://ui.persian-labs.ir/r/questionnaire.jsonUsage
import {
Questionnaire,
QuestionnaireActions,
QuestionnaireChoice,
QuestionnaireChoices,
QuestionnaireDescription,
QuestionnaireError,
QuestionnaireInput,
QuestionnaireItem,
QuestionnaireNext,
QuestionnairePrevious,
QuestionnaireProgress,
QuestionnaireSkip,
QuestionnaireSubmit,
QuestionnaireTitle,
} from "@/components/ui/questionnaire"
export function Example() {
return (
<Questionnaire>
<QuestionnaireProgress />
<QuestionnaireItem name="role" required>
<QuestionnaireTitle>What is your role?</QuestionnaireTitle>
<QuestionnaireChoices>
<QuestionnaireChoice value="developer">Developer</QuestionnaireChoice>
<QuestionnaireChoice value="designer">Designer</QuestionnaireChoice>
<QuestionnaireChoice value="other">Other</QuestionnaireChoice>
<QuestionnaireInput aria-label="Your role" placeholder="Your role..." />
</QuestionnaireChoices>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions>
<QuestionnairePrevious />
<div className="flex items-center gap-2">
<QuestionnaireSkip />
<QuestionnaireNext />
<QuestionnaireSubmit />
</div>
</QuestionnaireActions>
</Questionnaire>
)
}Examples
Multiple Selection
Pass multiple on QuestionnaireItem to render checkboxes and allow more than one answer.
Freeform Answer
Compose QuestionnaireInput alongside fixed QuestionnaireChoice items for an "other, please specify" pattern.
Explicit Skip
QuestionnaireSkip only renders for items that aren't required, letting the user move on without answering.
Shortcuts
Pass shortcuts="letters" (or "numbers") on Questionnaire to assign a keyboard shortcut to each choice.
Custom Validation
Drive QuestionnaireItem's invalid prop from an external schema — here, a zod schema validates the phone number on submit.
Controlled
Pass item and onItemChange to drive the active question from outside, such as an external step indicator.
Resume
Use defaultItem together with defaultChecked on each choice to restore a previously saved session.
Conditional Items
Toggle disabled on a later QuestionnaireItem based on an earlier answer to skip it from navigation entirely.
Navigation State
Every navigation part accepts a render function that receives state (status, disabled, visible, shortcut) for fully custom styling.
Custom Progress
QuestionnaireProgress also accepts a render function with current/total state, used here to build a segmented dot indicator instead of a bar.
Animated Items
The active item's data-active attribute toggles on each step change, which can retrigger an animate-in entrance — animating only opacity/transform, and respecting prefers-reduced-motion globally.
Dialog
Inside a Dialog, the host owns Cancel/Close — the questionnaire's own Submit closes the dialog through the host's controlled open state.
RTL
Navigation chevrons mirror, the progress bar fills from the reading start, and the choice indicator sits correctly regardless of direction.