Rendered from the shared Frontfriend stories with React Base, the same
preview Studio shows. Styling comes from your Design System; the code below
is native to Reka UI.
Installation
Complete project setup first. Your configuration
must select vue/reka and include your Design System ID.
pnpm exec frontfriend download questionnaire -f vue --primitive reka
Review dependencies reported by the downloader and keep your source
changes in version control.
Usage
<script setup lang="ts">
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireChoices } from "@/components/ui/questionnaire";
import { QuestionnaireChoice } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
</script>
<template>
<Questionnaire @submit.prevent>
<QuestionnaireItem name="useful" required>
<QuestionnaireTitle>Was this answer useful?</QuestionnaireTitle>
<QuestionnaireChoices>
<QuestionnaireChoice value="yes">Yes, it helped</QuestionnaireChoice>
<QuestionnaireChoice value="no">I need more detail</QuestionnaireChoice>
</QuestionnaireChoices>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions>
<QuestionnaireSubmit />
</QuestionnaireActions>
</Questionnaire>
</template>
Answers and validation
Set name on each Item and value on each Choice. Item is a fieldset and Title is a legend. Choice uses a native radio or checkbox. required removes Skip. An optional question needs an answer or an explicit skip. multiple allows several choices and a freeform answer. In single mode, a fixed choice and a freeform answer exclude each other. Input accepts native input constraints; its Item supplies name and form. noValidate defaults to true. Set it to false to check native constraints on selected answers. Error text is linked to the item.
Inactive items stay mounted, hidden and inert. Their answers remain in FormData. Use FormData.getAll() for repeated names. Native reset restores the initial item, default choices and default text. It also clears errors and skipped state. Supply items metadata for server rendering and the order of questions and shortcuts. Disabled items do not count toward progress. Check default selections after hydration when rendering on a server.
Keyboard
Up/Down moves among answers. Native radio keys also select. Left/Right changes questions outside radio and text inputs. Right never skips an unanswered item. Enter continues from a filled answer. Cmd/Ctrl+Enter validates, continues or submits. Letter and number shortcuts ignore text entry, IME, modified and repeated keys. Validation focuses the first invalid answer. Navigation focuses the next fieldset.
Vue bindings and slots
Use v-model:item (item and update:item) for controlled progress, v-model:checked (checked and update:checked) for Choice, and v-model (modelValue and update:modelValue) for Input. Choice also emits the native change event. Item emits statusChange and update:status with unanswered, answered or skipped. A rejected controlled update keeps the accepted value. The root emits submit and reset.
Scoped default slots expose current, total, first and last on Progress; active, disabled, invalid, multiple, required and status on Item; checked, disabled, invalid, shortcut and type on Choice; and disabled, shortcut, status and visible on navigation parts. Error exposes invalid. Reka Primitive parts accept as and asChild. See the props and events tables below.
Examples
Default
<script setup lang="ts">
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireProgress } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireChoices } from "@/components/ui/questionnaire";
import { QuestionnaireChoice } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnairePrevious } from "@/components/ui/questionnaire";
import { QuestionnaireSkip } from "@/components/ui/questionnaire";
import { QuestionnaireNext } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
</script>
<template>
<div
class="text-neutral-strong"
style="width: min(440px, calc(100vw - 32px)); display: grid; gap: 16px"
>
<Questionnaire @submit.prevent>
<QuestionnaireProgress />
<QuestionnaireItem name="useful" required>
<QuestionnaireTitle>Was this answer useful?</QuestionnaireTitle>
<QuestionnaireChoices>
<QuestionnaireChoice value="yes">Yes, it helped</QuestionnaireChoice>
<QuestionnaireChoice value="no"
>I need more detail</QuestionnaireChoice
>
</QuestionnaireChoices>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions
><QuestionnairePrevious /><QuestionnaireSkip /><QuestionnaireNext /><QuestionnaireSubmit
/></QuestionnaireActions>
</Questionnaire>
</div>
</template>
Single
<script setup lang="ts">
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireProgress } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireChoices } from "@/components/ui/questionnaire";
import { QuestionnaireChoice } from "@/components/ui/questionnaire";
import { QuestionnaireInput } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnairePrevious } from "@/components/ui/questionnaire";
import { QuestionnaireSkip } from "@/components/ui/questionnaire";
import { QuestionnaireNext } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
</script>
<template>
<div
class="text-neutral-strong"
style="width: min(440px, calc(100vw - 32px)); display: grid; gap: 16px"
>
<Questionnaire @submit.prevent>
<QuestionnaireProgress />
<QuestionnaireItem name="team" required>
<QuestionnaireTitle>Which team do you work with?</QuestionnaireTitle>
<QuestionnaireChoices>
<QuestionnaireChoice value="design">Design</QuestionnaireChoice>
<QuestionnaireChoice value="engineering"
>Engineering</QuestionnaireChoice
>
<QuestionnaireChoice value="research">Research</QuestionnaireChoice>
</QuestionnaireChoices>
<QuestionnaireInput
aria-label="Other team"
placeholder="Enter another team"
/>
<QuestionnaireError /> </QuestionnaireItem
><QuestionnaireItem name="project" required>
<QuestionnaireTitle>What is your project called?</QuestionnaireTitle>
<QuestionnaireInput
aria-label="Project name"
placeholder="Project name"
/>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions
><QuestionnairePrevious /><QuestionnaireSkip /><QuestionnaireNext /><QuestionnaireSubmit
/></QuestionnaireActions>
</Questionnaire>
</div>
</template>
Multiple
<script setup lang="ts">
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireProgress } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireDescription } from "@/components/ui/questionnaire";
import { QuestionnaireChoices } from "@/components/ui/questionnaire";
import { QuestionnaireChoice } from "@/components/ui/questionnaire";
import { QuestionnaireInput } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnairePrevious } from "@/components/ui/questionnaire";
import { QuestionnaireSkip } from "@/components/ui/questionnaire";
import { QuestionnaireNext } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
</script>
<template>
<div
class="text-neutral-strong"
style="width: min(440px, calc(100vw - 32px)); display: grid; gap: 16px"
>
<Questionnaire @submit.prevent>
<QuestionnaireProgress />
<QuestionnaireItem name="updates" required multiple>
<QuestionnaireTitle>Which updates do you want?</QuestionnaireTitle>
<QuestionnaireDescription
>Choose all that apply.</QuestionnaireDescription
><QuestionnaireChoices>
<QuestionnaireChoice value="releases"
>Product releases</QuestionnaireChoice
>
<QuestionnaireChoice value="guides"
>Practical guides</QuestionnaireChoice
>
<QuestionnaireChoice value="events"
>Events</QuestionnaireChoice
> </QuestionnaireChoices
><QuestionnaireInput
aria-label="Other update"
placeholder="Another topic"
/>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions
><QuestionnairePrevious /><QuestionnaireSkip /><QuestionnaireNext /><QuestionnaireSubmit
/></QuestionnaireActions>
</Questionnaire>
</div>
</template>
<script setup lang="ts">
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireProgress } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireInput } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnairePrevious } from "@/components/ui/questionnaire";
import { QuestionnaireSkip } from "@/components/ui/questionnaire";
import { QuestionnaireNext } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
</script>
<template>
<div
class="text-neutral-strong"
style="width: min(440px, calc(100vw - 32px)); display: grid; gap: 16px"
>
<Questionnaire :no-validate="false" @submit.prevent>
<QuestionnaireProgress />
<QuestionnaireItem name="project" required>
<QuestionnaireTitle>What is your project called?</QuestionnaireTitle>
<QuestionnaireInput
aria-label="Project name"
placeholder="Project name"
/>
<QuestionnaireError /> </QuestionnaireItem
><QuestionnaireItem name="website">
<QuestionnaireTitle>Where can we find your project?</QuestionnaireTitle>
<QuestionnaireInput
type="url"
aria-label="Website"
placeholder="https://example.com"
/>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions
><QuestionnairePrevious /><QuestionnaireSkip /><QuestionnaireNext /><QuestionnaireSubmit
/></QuestionnaireActions>
</Questionnaire>
</div>
</template>
Optional Skip
<script setup lang="ts">
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireProgress } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireDescription } from "@/components/ui/questionnaire";
import { QuestionnaireInput } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireChoices } from "@/components/ui/questionnaire";
import { QuestionnaireChoice } from "@/components/ui/questionnaire";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnairePrevious } from "@/components/ui/questionnaire";
import { QuestionnaireSkip } from "@/components/ui/questionnaire";
import { QuestionnaireNext } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
</script>
<template>
<div
class="text-neutral-strong"
style="width: min(440px, calc(100vw - 32px)); display: grid; gap: 16px"
>
<Questionnaire @submit.prevent>
<QuestionnaireProgress />
<QuestionnaireItem name="referral">
<QuestionnaireTitle>Who referred you?</QuestionnaireTitle>
<QuestionnaireDescription
>You can skip this question.</QuestionnaireDescription
><QuestionnaireInput aria-label="Referral" placeholder="Name or team" />
<QuestionnaireError /> </QuestionnaireItem
><QuestionnaireItem name="role" required>
<QuestionnaireTitle>Which role fits you?</QuestionnaireTitle>
<QuestionnaireChoices>
<QuestionnaireChoice value="builder">Builder</QuestionnaireChoice>
<QuestionnaireChoice value="reviewer">Reviewer</QuestionnaireChoice>
</QuestionnaireChoices>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions
><QuestionnairePrevious /><QuestionnaireSkip /><QuestionnaireNext /><QuestionnaireSubmit
/></QuestionnaireActions>
</Questionnaire>
</div>
</template>
Required Error
<script setup lang="ts">
import { ref } from "vue";
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireProgress } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireDescription } from "@/components/ui/questionnaire";
import { QuestionnaireInput } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnairePrevious } from "@/components/ui/questionnaire";
import { QuestionnaireSkip } from "@/components/ui/questionnaire";
import { QuestionnaireNext } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
const invalid = ref(true);
</script>
<template>
<div
class="text-neutral-strong"
style="width: min(440px, calc(100vw - 32px)); display: grid; gap: 16px"
>
<Questionnaire :no-validate="false" @submit.prevent>
<QuestionnaireProgress />
<QuestionnaireItem name="email" required :invalid="invalid">
<QuestionnaireTitle>What is your work email?</QuestionnaireTitle>
<QuestionnaireDescription
>Enter a valid email address to continue.</QuestionnaireDescription
><QuestionnaireInput
type="email"
aria-label="Work email"
placeholder="you@example.com"
@update:modelValue="invalid = false"
/>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions
><QuestionnairePrevious /><QuestionnaireSkip /><QuestionnaireNext /><QuestionnaireSubmit
/></QuestionnaireActions>
</Questionnaire>
</div>
</template>
Controlled Progress
<script setup lang="ts">
import { ref } from "vue";
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireProgress } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireChoices } from "@/components/ui/questionnaire";
import { QuestionnaireChoice } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireInput } from "@/components/ui/questionnaire";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnairePrevious } from "@/components/ui/questionnaire";
import { QuestionnaireSkip } from "@/components/ui/questionnaire";
import { QuestionnaireNext } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
const item = ref("team");
</script>
<template>
<div
class="text-neutral-strong"
style="width: min(440px, calc(100vw - 32px)); display: grid; gap: 16px"
>
<Questionnaire v-model:item="item" @submit.prevent>
<QuestionnaireProgress />
<QuestionnaireItem name="team" required>
<QuestionnaireTitle>Which team do you work with?</QuestionnaireTitle>
<QuestionnaireChoices>
<QuestionnaireChoice value="design">Design</QuestionnaireChoice>
<QuestionnaireChoice value="engineering"
>Engineering</QuestionnaireChoice
>
</QuestionnaireChoices>
<QuestionnaireError /> </QuestionnaireItem
><QuestionnaireItem name="project" required>
<QuestionnaireTitle>What is your project called?</QuestionnaireTitle>
<QuestionnaireInput
aria-label="Project name"
placeholder="Project name"
/>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions
><QuestionnairePrevious /><QuestionnaireSkip /><QuestionnaireNext /><QuestionnaireSubmit
/></QuestionnaireActions>
</Questionnaire>
<output aria-label="Controlled item">Current item: {{ item }}</output>
</div>
</template>
Letter Shortcuts
<script setup lang="ts">
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireProgress } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireDescription } from "@/components/ui/questionnaire";
import { QuestionnaireChoices } from "@/components/ui/questionnaire";
import { QuestionnaireChoice } from "@/components/ui/questionnaire";
import { QuestionnaireInput } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnairePrevious } from "@/components/ui/questionnaire";
import { QuestionnaireSkip } from "@/components/ui/questionnaire";
import { QuestionnaireNext } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
</script>
<template>
<div
class="text-neutral-strong"
style="width: min(440px, calc(100vw - 32px)); display: grid; gap: 16px"
>
<Questionnaire shortcuts="letters" @submit.prevent>
<QuestionnaireProgress />
<QuestionnaireItem name="priority" required>
<QuestionnaireTitle>What should we improve first?</QuestionnaireTitle>
<QuestionnaireDescription
>Press A, B or C outside the text input.</QuestionnaireDescription
><QuestionnaireChoices>
<QuestionnaireChoice value="speed">Speed</QuestionnaireChoice>
<QuestionnaireChoice value="access"
>Accessibility</QuestionnaireChoice
>
<QuestionnaireChoice value="docs"
>Documentation</QuestionnaireChoice
> </QuestionnaireChoices
><QuestionnaireInput
aria-label="Other priority"
placeholder="Another priority"
/>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions
><QuestionnairePrevious /><QuestionnaireSkip /><QuestionnaireNext /><QuestionnaireSubmit
/></QuestionnaireActions>
</Questionnaire>
</div>
</template>
Number Shortcuts
<script setup lang="ts">
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireProgress } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireDescription } from "@/components/ui/questionnaire";
import { QuestionnaireChoices } from "@/components/ui/questionnaire";
import { QuestionnaireChoice } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnairePrevious } from "@/components/ui/questionnaire";
import { QuestionnaireSkip } from "@/components/ui/questionnaire";
import { QuestionnaireNext } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
</script>
<template>
<div
class="text-neutral-strong"
style="width: min(440px, calc(100vw - 32px)); display: grid; gap: 16px"
>
<Questionnaire shortcuts="numbers" @submit.prevent>
<QuestionnaireProgress />
<QuestionnaireItem name="plan" required>
<QuestionnaireTitle>Which plan fits your team?</QuestionnaireTitle>
<QuestionnaireDescription
>Press 1, 2 or 3 to select.</QuestionnaireDescription
><QuestionnaireChoices>
<QuestionnaireChoice value="starter">Starter</QuestionnaireChoice>
<QuestionnaireChoice value="team">Team</QuestionnaireChoice>
<QuestionnaireChoice value="enterprise"
>Enterprise</QuestionnaireChoice
>
</QuestionnaireChoices>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions
><QuestionnairePrevious /><QuestionnaireSkip /><QuestionnaireNext /><QuestionnaireSubmit
/></QuestionnaireActions>
</Questionnaire>
</div>
</template>
Reset
<script setup lang="ts">
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireProgress } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireChoices } from "@/components/ui/questionnaire";
import { QuestionnaireChoice } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireInput } from "@/components/ui/questionnaire";
import { Button } from "@/components/ui/button";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnairePrevious } from "@/components/ui/questionnaire";
import { QuestionnaireSkip } from "@/components/ui/questionnaire";
import { QuestionnaireNext } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
</script>
<template>
<div
class="text-neutral-strong"
style="width: min(440px, calc(100vw - 32px)); display: grid; gap: 16px"
>
<Questionnaire default-item="team" @submit.prevent>
<QuestionnaireProgress />
<QuestionnaireItem name="team" required>
<QuestionnaireTitle>Which team do you work with?</QuestionnaireTitle>
<QuestionnaireChoices
><QuestionnaireChoice value="design" default-checked
>Design</QuestionnaireChoice
><QuestionnaireChoice value="engineering"
>Engineering</QuestionnaireChoice
></QuestionnaireChoices
>
<QuestionnaireError /> </QuestionnaireItem
><QuestionnaireItem name="project" required>
<QuestionnaireTitle>What is your project called?</QuestionnaireTitle>
<QuestionnaireInput aria-label="Project name" default-value="Atlas" />
<QuestionnaireError /> </QuestionnaireItem
><Button type="reset" variant="outline">Reset answers</Button>
<QuestionnaireActions
><QuestionnairePrevious /><QuestionnaireSkip /><QuestionnaireNext /><QuestionnaireSubmit
/></QuestionnaireActions>
</Questionnaire>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireProgress } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireDescription } from "@/components/ui/questionnaire";
import { QuestionnaireChoices } from "@/components/ui/questionnaire";
import { QuestionnaireChoice } from "@/components/ui/questionnaire";
import { QuestionnaireInput } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnairePrevious } from "@/components/ui/questionnaire";
import { QuestionnaireSkip } from "@/components/ui/questionnaire";
import { QuestionnaireNext } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
const submitted = ref<string | null>(null);
function submit(event: Event) {
submitted.value = JSON.stringify(
Array.from(new FormData(event.target as HTMLFormElement).entries()),
null,
2,
);
}
</script>
<template>
<div
class="text-neutral-strong"
style="width: min(440px, calc(100vw - 32px)); display: grid; gap: 16px"
>
<Questionnaire @submit.prevent="submit">
<QuestionnaireProgress />
<QuestionnaireItem name="team" required multiple>
<QuestionnaireTitle
>Which teams will use the project?</QuestionnaireTitle
>
<QuestionnaireDescription
>Choose one or more teams, or add another.</QuestionnaireDescription
><QuestionnaireChoices>
<QuestionnaireChoice value="design">Design</QuestionnaireChoice>
<QuestionnaireChoice value="engineering"
>Engineering</QuestionnaireChoice
> </QuestionnaireChoices
><QuestionnaireInput
aria-label="Other team"
placeholder="Another team"
/>
<QuestionnaireError /> </QuestionnaireItem
><QuestionnaireItem name="project" required>
<QuestionnaireTitle>What is your project called?</QuestionnaireTitle>
<QuestionnaireInput
aria-label="Project name"
placeholder="Project name"
/>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions
><QuestionnairePrevious /><QuestionnaireSkip /><QuestionnaireNext /><QuestionnaireSubmit
/></QuestionnaireActions>
</Questionnaire>
<output v-if="submitted" aria-label="Submitted answers" role="status"
>Submitted answers
<pre style="white-space: pre-wrap; overflow-wrap: anywhere">{{
submitted
}}</pre>
</output>
</div>
</template>
Disabled
<script setup lang="ts">
import { Questionnaire } from "@/components/ui/questionnaire";
import { QuestionnaireProgress } from "@/components/ui/questionnaire";
import { QuestionnaireItem } from "@/components/ui/questionnaire";
import { QuestionnaireTitle } from "@/components/ui/questionnaire";
import { QuestionnaireChoices } from "@/components/ui/questionnaire";
import { QuestionnaireChoice } from "@/components/ui/questionnaire";
import { QuestionnaireError } from "@/components/ui/questionnaire";
import { QuestionnaireInput } from "@/components/ui/questionnaire";
import { QuestionnaireActions } from "@/components/ui/questionnaire";
import { QuestionnairePrevious } from "@/components/ui/questionnaire";
import { QuestionnaireSkip } from "@/components/ui/questionnaire";
import { QuestionnaireNext } from "@/components/ui/questionnaire";
import { QuestionnaireSubmit } from "@/components/ui/questionnaire";
const args = {
} satisfies Partial<
InstanceType<typeof Questionnaire> extends { $props: infer Props }
? Props
: {}
> &
Record<string, unknown>;
</script>
<template>
<div
class="text-neutral-strong"
style="width: min(440px, calc(100vw - 32px)); display: grid; gap: 16px"
>
<Questionnaire @submit.prevent>
<QuestionnaireProgress />
<QuestionnaireItem name="team" required>
<QuestionnaireTitle>Which team do you work with?</QuestionnaireTitle>
<QuestionnaireChoices
><QuestionnaireChoice value="design">Design</QuestionnaireChoice
><QuestionnaireChoice value="research" disabled
>Research (unavailable)</QuestionnaireChoice
></QuestionnaireChoices
>
<QuestionnaireError /> </QuestionnaireItem
><QuestionnaireItem name="internal" disabled>
<QuestionnaireTitle>Internal review only</QuestionnaireTitle>
<QuestionnaireInput aria-label="Internal note" />
<QuestionnaireError /> </QuestionnaireItem
><QuestionnaireItem name="project" required>
<QuestionnaireTitle>What is your project called?</QuestionnaireTitle>
<QuestionnaireInput
aria-label="Project name"
placeholder="Project name"
/>
<QuestionnaireError />
</QuestionnaireItem>
<QuestionnaireActions
><QuestionnairePrevious /><QuestionnaireSkip /><QuestionnaireNext /><QuestionnaireSubmit
/></QuestionnaireActions>
</Questionnaire>
</div>
</template>
API reference
Questionnaire API from the shadcn-vue port. Native HTML attributes and Reka Primitive composition remain available as listed below.
Questionnaire
| Prop | Type | Default | Description |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
defaultItem | string | First enabled item | Initial active item for uncontrolled progress. Reset restores this item. |
item | string | — | Controlled active item name. Accept the change event to move to another item. |
items | readonly QuestionnaireItemDefinition[] | DOM order | Metadata for server rendering and item order. Each entry has name, required?, disabled?, and choices?: { value, disabled? }[]. |
noValidate | boolean | true | When false, validate native constraints on selected answers. |
reset | (event: Event) => void | — | Emitted event. Native reset event. Restores initial answers and clears item status. |
shortcuts | "letters" | "numbers" | Off | Choice shortcuts. Omit this prop to turn shortcuts off. |
submit | (event: Event) => void | — | Emitted event. Native submit event after validation. Read the form with FormData. |
update:item | (item: string) => void | — | Emitted event. Requests the next active item. Bind with v-model:item. |
QuestionnaireActions
| Prop | Type | Default | Description |
as | string | Component | "div" | Element or component used by Reka Primitive. |
asChild | boolean | false | Merge the part into its single child element. |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
QuestionnaireChoice
| Prop | Type | Default | Description |
change | (event: Event) => void | — | Emitted event. Native input change event. Prevent its default action to reject selection. |
checked | boolean | — | Controlled selected state. Accept the change event to update it. |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
defaultChecked | boolean | false | Initial selected state restored by reset. |
disabled | boolean | false | Disable this choice and exclude it from shortcuts. |
update:checked | (checked: boolean) => void | — | Emitted event. Requests the selected state. Bind with v-model:checked. |
value | string | — | Value submitted under the item name when this choice is selected. |
QuestionnaireChoiceDescription
| Prop | Type | Default | Description |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
QuestionnaireChoices
| Prop | Type | Default | Description |
as | string | Component | "div" | Element or component used by Reka Primitive. |
asChild | boolean | false | Merge the part into its single child element. |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
QuestionnaireDescription
| Prop | Type | Default | Description |
as | string | Component | "p" | Element or component used by Reka Primitive. |
asChild | boolean | false | Merge the part into its single child element. |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
id | string | Generated ID | ID registered with the item for its accessible label or description. |
QuestionnaireError
| Prop | Type | Default | Description |
as | string | Component | "p" | Element or component used by Reka Primitive. |
asChild | boolean | false | Merge the part into its single child element. |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
id | string | Generated ID | ID registered with the item for its accessible label or description. |
QuestionnaireInput
| Prop | Type | Default | Description |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
defaultValue | string | number | "" | Initial text restored by reset. |
disabled | boolean | false | Disable this input and exclude its value from the answer. |
modelValue | string | number | — | Controlled input value. Use v-model to accept update:modelValue. |
type | "date" | "datetime-local" | "email" | "month" | "number" | "password" | "search" | "tel" | "text" | "time" | "url" | "week" | "text" | Native text-entry input type. The item supplies name and form. |
update:modelValue | (value: string) => void | — | Emitted event. Requests the input text. Bind with v-model. |
QuestionnaireItem
| Prop | Type | Default | Description |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
disabled | boolean | false | Exclude this item from progress, navigation, validation and submitted answers. |
invalid | boolean | false | Mark an external validation error. |
multiple | boolean | false | Use checkboxes and retain multiple fixed choices and freeform text. |
name | string | — | Unique item name. Also supplies the native form name for its answers. |
required | boolean | false | Require an answer and hide Skip. |
statusChange | (status: "unanswered" | "answered" | "skipped") => void | — | Emitted event. Reports the current item status. |
update:status | (status: "unanswered" | "answered" | "skipped") => void | — | Emitted event. Reports the current item status. Retained for shadcn-vue compatibility. |
QuestionnaireNext
| Prop | Type | Default | Description |
as | string | Component | "button" | Element or component used by Reka Primitive. |
asChild | boolean | false | Merge the part into its single child element. |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
click | (event: MouseEvent) => void | — | Emitted event. Native button click. Prevent its default action to cancel navigation or submission. |
disabled | boolean | false | Disable this navigation button. |
size | ButtonVariants["size"] | "default" | Button size from the design system. |
variant | ButtonVariants["variant"] | "default" | Button appearance from the design system. |
QuestionnairePrevious
| Prop | Type | Default | Description |
as | string | Component | "button" | Element or component used by Reka Primitive. |
asChild | boolean | false | Merge the part into its single child element. |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
click | (event: MouseEvent) => void | — | Emitted event. Native button click. Prevent its default action to cancel navigation or submission. |
disabled | boolean | false | Disable this navigation button. |
size | ButtonVariants["size"] | "default" | Button size from the design system. |
variant | ButtonVariants["variant"] | "outline" | Button appearance from the design system. |
QuestionnaireProgress
| Prop | Type | Default | Description |
as | string | Component | "div" | Element or component used by Reka Primitive. |
asChild | boolean | false | Merge the part into its single child element. |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
QuestionnaireSkip
| Prop | Type | Default | Description |
as | string | Component | "button" | Element or component used by Reka Primitive. |
asChild | boolean | false | Merge the part into its single child element. |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
click | (event: MouseEvent) => void | — | Emitted event. Native button click. Prevent its default action to cancel navigation or submission. |
disabled | boolean | false | Disable this navigation button. |
size | ButtonVariants["size"] | "default" | Button size from the design system. |
variant | ButtonVariants["variant"] | "outline" | Button appearance from the design system. |
QuestionnaireSubmit
| Prop | Type | Default | Description |
as | string | Component | "button" | Element or component used by Reka Primitive. |
asChild | boolean | false | Merge the part into its single child element. |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
click | (event: MouseEvent) => void | — | Native event. The default button receives click listeners through Vue attribute forwarding. Prevent the default action to cancel submission. |
disabled | boolean | false | Disable this navigation button. |
size | ButtonVariants["size"] | "default" | Button size from the design system. |
variant | ButtonVariants["variant"] | "default" | Button appearance from the design system. |
QuestionnaireTitle
| Prop | Type | Default | Description |
as | string | Component | "legend" | Element or component used by Reka Primitive. |
asChild | boolean | false | Merge the part into its single child element. |
class | HTMLAttributes["class"] | — | Additional classes on this part. |
id | string | Generated ID | ID registered with the item for its accessible label or description. |
Configuration determines the actual classes and available styling hooks.
See Theming & component configuration before introducing
a repeated local override.