Sabil UI

Search documentation

Search for a command to run...

Components

Questionnaire

A step-by-step set of questions with keyboard shortcuts.

Question 1 of 3
What should the agent build next?

Choose a direction or describe another task.

Installation#

  1. Install the dependencies

    npm install @shadcn/react cn lucide-react
  2. Copy the source into your project

    components/ui/questionnaire.tsx
    "use client"import * as React from "react"import { Questionnaire as QuestionnairePrimitive } from "@shadcn/react/questionnaire"import { cn } from "cn"import { buttonVariants, type Button } from "@/components/ui/button"import { CheckIcon } from "lucide-react"function Questionnaire({  className,  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Root>) {  return (    <QuestionnairePrimitive.Root      data-slot="questionnaire"      className={cn("flex w-full min-w-0 flex-col gap-4", className)}      {...props}    />  )}function QuestionnaireProgress({  className,  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Progress>) {  return (    <QuestionnairePrimitive.Progress      data-slot="questionnaire-progress"      className={cn(        "min-h-[1lh] w-fit min-w-[14ch] text-xs font-medium text-muted-foreground tabular-nums",        className      )}      {...props}    />  )}function QuestionnaireItem({  className,  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Item>) {  return (    <QuestionnairePrimitive.Item      data-slot="questionnaire-item"      className={cn(        "flex min-w-0 flex-col gap-4 border-0 p-0 outline-none",        className      )}      {...props}    />  )}function QuestionnaireTitle({  className,  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Title>) {  return (    <QuestionnairePrimitive.Title      data-slot="questionnaire-title"      className={cn(        "font-heading text-base leading-snug font-medium text-pretty [&:not(:has(~[data-slot=questionnaire-description]))]:mb-4",        className      )}      {...props}    />  )}function QuestionnaireDescription({  className,  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Description>) {  return (    <QuestionnairePrimitive.Description      data-slot="questionnaire-description"      className={cn("text-sm text-pretty text-muted-foreground", className)}      {...props}    />  )}function QuestionnaireChoices({  className,  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Choices>) {  return (    <QuestionnairePrimitive.Choices      data-slot="questionnaire-choices"      className={cn(        "group/questionnaire-choices grid min-w-0 gap-2",        className      )}      {...props}    />  )}function QuestionnaireChoice({  children,  className,  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Choice>) {  return (    <QuestionnairePrimitive.Choice      data-slot="questionnaire-choice"      className={cn(        "group/questionnaire-choice relative flex min-h-11 cursor-pointer items-start gap-2.5 rounded-lg border border-input bg-transparent px-3 py-2.5 text-start text-sm transition-colors outline-none select-none hover:bg-muted/50 has-[>input:focus-visible]:border-ring has-[>input:focus-visible]:ring-3 has-[>input:focus-visible]:ring-ring/50 data-invalid:border-destructive dark:bg-input/20 data-checked:border-primary/40 data-checked:bg-muted dark:data-checked:bg-muted",        "data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-disabled:opacity-50",        className      )}      {...props}    >      <QuestionnairePrimitive.ChoiceInput        data-slot="questionnaire-choice-input"        className="absolute inset-0 z-10 size-full cursor-pointer opacity-0"      />      <span        aria-hidden="true"        data-slot="questionnaire-choice-indicator"        className="pointer-events-none relative flex size-4 shrink-0 translate-y-[--spacing(0.45)] items-center justify-center rounded-[4px] border border-input group-has-data-[slot=questionnaire-choice-description]/questionnaire-choice:translate-y-0.5 group-data-[type=radio]/questionnaire-choice:rounded-full group-data-checked/questionnaire-choice:border-primary group-data-checked/questionnaire-choice:bg-primary group-data-checked/questionnaire-choice:text-primary-foreground dark:bg-input/30 dark:group-data-checked/questionnaire-choice:bg-primary"      >        <span          data-slot="questionnaire-choice-indicator-dot"          className="hidden size-2 rounded-full bg-primary-foreground group-data-[type=checkbox]/questionnaire-choice:hidden group-data-checked/questionnaire-choice:block"        />        <CheckIcon data-slot="questionnaire-choice-indicator-check" className="hidden size-3.5 group-data-[type=radio]/questionnaire-choice:hidden group-data-checked/questionnaire-choice:block" />      </span>      <QuestionnairePrimitive.ChoiceLabel        data-slot="questionnaire-choice-label"        className="flex min-w-0 flex-1 flex-col gap-0.5 leading-snug"      >        {children}      </QuestionnairePrimitive.ChoiceLabel>      <QuestionnairePrimitive.ChoiceShortcut        data-slot="questionnaire-choice-shortcut"        className="pointer-events-none ms-auto hidden size-5 shrink-0 translate-y-[--spacing(0.45)] items-center justify-center rounded-md border border-input bg-background font-mono text-[0.625rem] leading-none font-medium text-muted-foreground group-has-data-[slot=questionnaire-choice-description]/questionnaire-choice:translate-y-0.5 group-data-[shortcut]/questionnaire-choice:inline-flex"      />    </QuestionnairePrimitive.Choice>  )}function QuestionnaireChoiceDescription({  className,  ...props}: React.ComponentProps<"span">) {  return (    <span      data-slot="questionnaire-choice-description"      className={cn("text-muted-foreground", className)}      {...props}    />  )}function QuestionnaireInput({  className,  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Input>) {  return (    <div      data-slot="questionnaire-input-wrapper"      className="group/questionnaire-input relative w-full min-w-0"    >      <QuestionnairePrimitive.Input        data-slot="questionnaire-input"        className={cn(          "h-8 min-h-11 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-[color,box-shadow,background-color] outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 sm:min-h-0 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",          "selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground",          className        )}        {...props}      />    </div>  )}function QuestionnaireError({  className,  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Error>) {  return (    <QuestionnairePrimitive.Error      data-slot="questionnaire-error"      className={cn("mt-2 text-sm text-destructive", className)}      {...props}    />  )}function QuestionnaireActions({  className,  ...props}: React.ComponentProps<"div">) {  return (    <div      data-slot="questionnaire-actions"      className={cn(        "grid min-h-11 w-full grid-cols-[minmax(0,1fr)_auto_auto] items-center gap-2 sm:min-h-8",        className      )}      {...props}    />  )}function QuestionnairePrevious({  children,  className,  size = "default",  variant = "outline",  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Previous> &  Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {  return (    <QuestionnairePrimitive.Previous      data-slot="questionnaire-previous"      data-size={size}      data-variant={variant}      className={cn(        buttonVariants({ size, variant }),        "col-start-1 row-start-1 min-h-11 justify-self-start sm:min-h-0",        className      )}      {...props}    >      {children ?? "Previous"}    </QuestionnairePrimitive.Previous>  )}function QuestionnaireSkip({  children,  className,  size = "default",  variant = "outline",  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Skip> &  Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {  return (    <QuestionnairePrimitive.Skip      data-slot="questionnaire-skip"      data-size={size}      data-variant={variant}      className={cn(        buttonVariants({ size, variant }),        "col-start-2 row-start-1 min-h-11 justify-self-end sm:min-h-0",        className      )}      {...props}    >      {children ?? "Skip"}    </QuestionnairePrimitive.Skip>  )}function QuestionnaireNext({  children,  className,  size = "default",  variant = "default",  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Next> &  Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {  return (    <QuestionnairePrimitive.Next      data-slot="questionnaire-next"      data-size={size}      data-variant={variant}      className={cn(        buttonVariants({ size, variant }),        "col-start-3 row-start-1 min-h-11 justify-self-end sm:min-h-0",        className      )}      {...props}    >      {children ?? "Next"}    </QuestionnairePrimitive.Next>  )}function QuestionnaireSubmit({  children,  className,  size = "default",  variant = "default",  ...props}: React.ComponentProps<typeof QuestionnairePrimitive.Submit> &  Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {  return (    <QuestionnairePrimitive.Submit      data-slot="questionnaire-submit"      data-size={size}      data-variant={variant}      className={cn(        buttonVariants({ size, variant }),        "col-start-3 row-start-1 min-h-11 justify-self-end sm:min-h-0",        className      )}      {...props}    >      {children ?? "Submit"}    </QuestionnairePrimitive.Submit>  )}export {  Questionnaire,  QuestionnaireActions,  QuestionnaireChoice,  QuestionnaireChoiceDescription,  QuestionnaireChoices,  QuestionnaireDescription,  QuestionnaireError,  QuestionnaireInput,  QuestionnaireItem,  QuestionnaireNext,  QuestionnairePrevious,  QuestionnaireProgress,  QuestionnaireSkip,  QuestionnaireSubmit,  QuestionnaireTitle,}export type { QuestionnaireItemStatus } from "@shadcn/react/questionnaire"
  3. Make sure the foundation is in place

    Components read their colors, radius and surfaces from the tokens in app/globals.css. See Theming.

Usage#

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

Examples#

Animated

Question 1 of 3
What should the agent do?

Choose the task for this run.

Card

What should the agent work on?
Choose the task that should be handled next.
Question 1 of 2

Conditional

Question 1 of 2
Where should the agent run?

Cloud runs add an environment question to this flow.

Controlled

Current checkpoint: Change scope

Question 1 of 3
What may the agent change?

The host stores the active checkpoint while Questionnaire navigates.

Dialog

Freeform

How should the agent approach this refactor?

Choose a strategy or write a more specific instruction.

Multiple

What context should the agent inspect?

Select every source that may affect the implementation.

Navigation State

Question 1 of 2
What may the agent modify?

Next is intentionally disabled until an answer is selected.

Progress

Checkpoint 1 of 4
How large is the change?

Resume

Question 2 of 3
How should the migration be verified?

These checks were selected during the previous session.

Shortcuts

What should the agent do next?

Use the displayed shortcut or navigate with the keyboard.

Skip

Question 1 of 3
What kind of change is this?

Choose the category that best describes the work.

Validation

How much detail should the answer include?

Choose the response depth.

1 / 2