Questionnaire: HextaUI e shadcn/ui

O Questionnaire do HextaUI mantém os nomes e tokens de tema do shadcn/ui e instala com a mesma CLI. Esta página lista o que muda, lido do código de ambos. O lado do shadcn/ui é o registro base-vega em 2026-10-16.

Visão geral

MedidaHextaUIshadcn/ui
Partes exportadas1515
Props tratadas pelo wrapper188
Linhas de código942332
Dependências do npm@shadcn/[email protected]@tabler/icons-reactclass-variance-authoritycn@shadcn/reactcn
Construído combuttoninputkbdmotionnumber-flowthemebutton

Mais código não é melhor por si só. A maior parte das linhas extras do HextaUI cuida de estados, animações e casos extremos que você escreveria por conta própria.

Props que o HextaUI adiciona

Props que o próprio wrapper do HextaUI trata e o do shadcn/ui não. Props repassadas direto ao Base UI ou à biblioteca por baixo ficam de fora, porque as duas versões as aceitam.

PropO que faz
transitionQuestionnaireVer a referência da API
autoAdvanceQuestionnaireAvança 350ms depois que uma resposta única é escolhida pela primeira vez.
variantQuestionnaireProgressVer a referência da API

A versão do HextaUI

Question 1 of 3
What best describes your work?

We'll tailor the examples you see first.

"use client"

import * as React from "react"

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

const items = [
  {
    name: "role",
    required: true,
    prompt: "What best describes your work?",
    description: "We'll tailor the examples you see first.",
    choices: [
      {
        value: "design",
        label: "Design",
        description: "Interfaces, systems and prototypes.",
      },
      {
        value: "engineering",
        label: "Engineering",
        description: "Shipping and maintaining product code.",
      },
      {
        value: "both",
        label: "A bit of both",
        description: "Design engineering, the best kind.",
      },
    ],
  },
  {
    name: "interests",
    multiple: true,
    required: true,
    prompt: "Which components do you reach for most?",
    description: "Pick as many as you like.",
    choices: [
      { value: "forms", label: "Forms and inputs" },
      { value: "overlays", label: "Dialogs and sheets" },
      { value: "navigation", label: "Menus and navigation" },
      { value: "data", label: "Tables and data" },
    ],
  },
  {
    name: "source",
    prompt: "How did you find HextaUI?",
    description: "Optional. Skip it if you don't remember.",
    choices: [
      { value: "x", label: "X / Twitter" },
      { value: "github", label: "GitHub" },
      { value: "friend", label: "A friend" },
    ],
    input: { label: "Somewhere else", placeholder: "Somewhere else…" },
  },
] as const

export function QuestionnaireDemo() {
  const [answers, setAnswers] = React.useState<[string, string][] | null>(null)

  if (answers) {
    return (
      <div className="flex w-full max-w-md flex-col items-start gap-4 rounded-xl border p-5">
        <div className="flex flex-col gap-1">
          <p className="font-medium">Thanks, that helps a lot.</p>
          <p className="text-sm text-muted-foreground">
            Here&apos;s what the form sent:
          </p>
        </div>
        <ul className="flex w-full flex-col gap-1 font-mono text-xs">
          {answers.map(([name, value], index) => (
            <li key={index} className="flex justify-between gap-4">
              <span className="text-muted-foreground">{name}</span>
              <span className="truncate">{value}</span>
            </li>
          ))}
        </ul>
        <Button variant="outline" size="sm" onClick={() => setAnswers(null)}>
          Start over
        </Button>
      </div>
    )
  }

  return (
    <div className="w-full max-w-md rounded-xl border p-5">
      <Questionnaire
        items={items}
        shortcuts="letters"
        onSubmit={(event) => {
          event.preventDefault()
          const data = new FormData(event.currentTarget)
          setAnswers(
            Array.from(data.entries(), ([name, value]) => [name, String(value)])
          )
        }}
      >
        <QuestionnaireProgress />
        {items.map((item) => (
          <QuestionnaireItem
            key={item.name}
            name={item.name}
            required={"required" in item && item.required}
            multiple={"multiple" in item && item.multiple}
          >
            <QuestionnaireTitle>{item.prompt}</QuestionnaireTitle>
            <QuestionnaireDescription>
              {item.description}
            </QuestionnaireDescription>
            <QuestionnaireChoices>
              {item.choices.map((choice) => (
                <QuestionnaireChoice key={choice.value} value={choice.value}>
                  <span className="font-medium">{choice.label}</span>
                  {"description" in choice ? (
                    <QuestionnaireChoiceDescription>
                      {choice.description}
                    </QuestionnaireChoiceDescription>
                  ) : null}
                </QuestionnaireChoice>
              ))}
              {"input" in item ? (
                <QuestionnaireInput
                  aria-label={item.input.label}
                  placeholder={item.input.placeholder}
                />
              ) : null}
            </QuestionnaireChoices>
            <QuestionnaireError />
          </QuestionnaireItem>
        ))}
        <QuestionnaireActions>
          <QuestionnairePrevious />
          <QuestionnaireSkip />
          <QuestionnaireNext />
          <QuestionnaireSubmit />
        </QuestionnaireActions>
      </Questionnaire>
    </div>
  )
}

Todos os estados, o mapa de teclado e a referência da API estão na página do componente.

4 dos seus exemplos também têm uma página própria.

Troque um componente

Rode o comando add com --overwrite para substituir o arquivo do shadcn/ui em @/components/ui e revise o diff no git antes de mantê-lo. Os imports continuam iguais e os componentes que você não trocar seguem funcionando.

pnpm dlx shadcn@latest add https://hextaui.com/r/questionnaire.json --overwrite
  • Cada prop da tabela acima é nova, então o código escrito para o shadcn/ui ainda não a passa.
  • Antes de trocar, confira qualquer parte ou variante listada só para o shadcn/ui e associe-a à mais próxima do HextaUI.

Outros componentes comparados

Mais comparações